Skip to main content
Engineering LibreTexts

9.4: Synchronization errors

  • Page ID
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)

    The problem with the previous program is that the children access the shared variable, counter, without synchronization, so several threads can read the same value of counter before any threads increment it.

    Here is a sequence of events that could explain the output in the previous section:

    Child A reads 0
    Child B reads 0
    Child C reads 0
    Child A prints   0
    Child B prints   0
    Child A sets counter=1
    Child D reads 1
    Child D prints   1
    Child C prints   0
    Child A sets counter=1
    Child B sets counter=2
    Child C sets counter=3
    Child E reads 3
    Child E prints   3
    Child D sets counter=4
    Child E sets counter=5

    Each time you run the program, threads might be interrupted at different points, or the scheduler might choose different threads to run, so the sequence of events, and the results, will be different.

    Suppose we want to impose some order. For example, we might want each thread to read a different value of counter and increment it, so that the value of counter reflects the number of threads that have executed child_code.

    To enforce that requirement, we can use a “mutex”, which is an object that guarantees “mutual exclusion” for a block of code; that is, only one thread can execute the block at a time.

    I have written a small module called mutex.c that provides mutex objects. I’ll show you how to use it first; then I’ll explain how it works.

    Here’s a version of child_code that uses a mutex to synchronize threads:

    void child_code(Shared *shared)
        printf("counter = %d\n", shared->counter);

    Before any thread can access counter, it has to “lock” the mutex, which has the effect of barring all other threads. Suppose Thread A has locked the mutex and is in the middle of child_code. If Thread B arrives and executes mutex_lock, it blocks.

    When Thread A is done, it executes mutex_unlock, which allows Thread B to proceed. In effect, the threads line up to execute child_code one at a time, so they can’t interfere with each other. When I run this code with 5 children, I get:

    counter = 0
    counter = 1
    counter = 2
    counter = 3
    counter = 4

    And that satisfies the requirements. In order for this solution to work, I have to add the Mutex to the Shared struct:

    typedef struct {
        int counter;
        Mutex *mutex;
    } Shared;

    And initialize it in make_shared

    Shared *make_shared(int end)
        Shared *shared = check_malloc(sizeof(Shared));
        shared->counter = 0;
        shared->mutex = make_mutex();   //-- this line is new
        return shared;

    The code in this section is in counter_mutex.c. The definition of Mutex is in mutex.c, which I explain in the next section.

    9.4: Synchronization errors is shared under a CC BY-NC license and was authored, remixed, and/or curated by Allen B. Downey (Green Tea Press) .

    • Was this article helpful?