This lesson is in the early stages of development (Alpha version)

Synchronization

Overview

Teaching: 10 min
Exercises: 5 min
Questions
  • How to use OpenMP synchronization directives

Objectives
  • Use master, barrier and critical constructs to better control our threads are synchronized

Synchronization

As we have seen previously, sometimes we need to force a thread to ‘lock-out’ other threads for a period of time when operating on shared variables, or wait until other threads have caught up to ensure that a calculation has been completed before continuing. OpenMP directives useful for this purpose are:

Let us take a look at the following example. In this case, the serial version of this code simply increments a counter by 1, prints the thread ID number, waits for 10 seconds and confirms that the program finished.

We might want to rewrite this code to work in parallel. Perhaps our counter is keeping track of how many threads have finished successfully, or perhaps it represents a line in a text file where every entry is the name of a data file that needs to be processed. In any case, we might want to ensure that it is updated carefully.

Example (serial)

// Load the OpenMP functions library
#include<omp.h>

int main()
{
  // Set and initialise variables
  int tnum=0, incr=0;

    // Start a critical block that we want only one thread to access
    // at a time. Note that the 'incr' variable is NOT private!
      incr = incr + 1;

    // The master thread prints out the results of the calculation and
    // then does some other processing that the other threads have to
    // wait for.
      tnum = omp_get_thread_num();
      printf("Master thread is number %d\n", tnum);
      printf("Summation = %d\n", incr);
      sleep (10);

    // Ensure ALL threads have finished their processing before continuing.
      printf("finished!\n");

  return 0;
}

Example (parallel)

// Load the OpenMP functions library
#include<omp.h>

int main()
{
   // Set and initialise variables
   int tnum=0, incr=0;

   // Start parallel block
   #pragma omp parallel private(tnum)
   {
       // Start a critical block that we want only one thread to access
       // at a time. Note that the 'incr' variable is NOT private!
       #pragma omp critical
       {
           incr = incr + 1;
       }
       #pragma omp barrier 
       // The master thread prints out the results of the calculation and
       // then does some other processing that the other threads have to
       // wait for.
       #pragma omp master
       {
           tnum = omp_get_thread_num();
           printf("Master thread is number %d\n", tnum);
           printf("Summation = %d\n", incr);
           sleep (10);
       }

       // Ensure ALL threads have finished their processing before continuing.
       #pragma omp barrier
       {
           printf("finished!\n");
       }
   }

   return 0;
}

More race conditions

Let us begin by confirming that our code behaves in serial mode in the way we expect. For this you don’t need to make any modifications since the OpenMP directives are commented out at the moment. Go ahead and compile the code:

~/openmp/Intro_to_OpenMP.2019/c$ icc -qopenmp -o sync sync.c

Once we are satisfied with the serial version of the code, uncomment the OpenMP directives that create the parallel region. Recompile and run again. How does it behave? How can it be improved?

Key Points

  • OpenMP constructs master, barrier and critical are useful to define sections and points in our code where threads should synchronize with each other