Synchronization constructs are used to ensure the consistency of shared data and to coordinate parallel execution among threads.
The synchronization constructs are:
Use the ATOMIC directive to ensure that a specific memory location is updated atomically instead of exposing the location to the possibility of multiple, simultaneously writing threads.
This directive applies only to the immediately following statement, which must have one of the following forms:
x = x operator expr
x = expr operator x
x = intrinsic (x, expr)
x = intrinsic (expr, x)
In the preceding statements:
x is a scalar variable of intrinsic type
expr is a scalar expression that does not reference x
intrinsic is either MAX, MIN, IAND, IOR, or IEOR
operator is either +, *, -, /, .AND., .OR., .EQV., or .NEQV.
This directive permits optimization beyond that of a critical section around the assignment. An implementation can replace all ATOMIC directives by enclosing the statement in a critical section. All of these critical sections must use the same unique name.
Only the load and store of x are atomic; the evaluation of expr is not atomic. To avoid race conditions, all updates of the location in parallel must be protected by using the ATOMIC directive, except those that are known to be free of race conditions. The function intrinsic, the operator operator, and the assignment must be the intrinsic function, operator, and assignment.
This restriction applies to the ATOMIC directive: All references to storage location x must have the same type parameters.
In the following example, the collection of Y locations is updated atomically:
Y = Y + B(I)
To synchronize all threads within a parallel region, use the BARRIER directive. You can use this directive only within a parallel region defined by using the PARALLEL directive. You cannot use the BARRIER directive within the DO, PARALLEL DO, SECTIONS, PARALLEL SECTIONS, and SINGLE directives.
When encountered, each thread waits at the BARRIER directive until all threads have reached the directive.
In the following example, the BARRIER directive ensures that all threads have executed the first loop and that it is safe to execute the second loop:
c$OMP DO PRIVATE(i)
DO i = 1, 100
b(i) = i
c$OMP DO PRIVATE(i)
DO i = 1, 100
a(i) = b(101-i)
c$OMP END PARALLEL
Use the CRITICAL and END CRITICAL directives to restrict access to a block of code, referred to as a critical section, to one thread at a time.
A thread waits at the beginning of a critical section until no other thread in the team is executing a critical section having the same name.
When a thread enters the critical section, a latch variable is set to closed and all other threads are locked out. When the thread exits the critical section at the END CRITICAL directive, the latch variable is set to open, allowing another thread access to the critical section.
If you specify a critical section name in the CRITICAL directive, you must specify the same name in the END CRITICAL directive. If you do not specify a name for the CRITICAL directive, you cannot specify a name for the END CRITICAL directive.
All unnamed CRITICAL directives map to the same name. Critical section names are global to the program.
The following example includes several CRITICAL directives, and illustrates a queuing model in which a task is dequeued and worked on. To guard against multiple threads dequeuing the same task, the dequeuing operation must be in a critical section. Because there are two independent queues in this example, each queue is protected by CRITICAL directives having different names, X_AXIS and Y_AXIS, respectively:!$OMP PARALLEL DEFAULT(PRIVATE,SHARED(X,Y)
Unnamed critical sections use the global lock from the Pthread package. This allows you to synchronize with other code by using the same lock. Named locks are created and maintained by the compiler and can be significantly more efficient.
Use the FLUSH directive to identify a synchronization point at which a consistent view of memory is provided. Thread-visible variables are written back to memory at this point.
To avoid flushing all thread-visible variables at this point, include a list of comma-separated named variables to be flushed.
The following example uses the FLUSH directive for point-to-point synchronization between thread 0 and thread 1 for the variable ISYNC:
!$OMP PARALLEL DEFAULT(PRIVATE),SHARED(ISYNC)
IAM = OMP_GET_THREAD_NUM()
ISYNC(IAM) = 0
! I Am Done With My Work, Synchronize With My Neighbor
ISYNC(IAM) = 1
! Wait Till Neighbor Is Done
DO WHILE (ISYNC(NEIGH) .EQ. 0)
!$OMP END PARALLEL
Use the MASTER and END MASTER directives to identify a block of code that is executed only by the master thread.
The other threads of the team skip the code and continue execution. There is no implied barrier at the END MASTER directive.
In the following example, only the master thread executes the routines OUTPUT and INPUT:
!$OMP PARALLEL DEFAULT(SHARED)
!$OMP END MASTER
!$OMP END PARALLEL
Use the ORDERED and END ORDERED directives within a DO construct to allow work within an ordered section to execute sequentially while allowing work outside the section to execute in parallel.
When you use the ORDERED directive, you must also specify the ORDERED clause on the DO directive.
Only one thread at a time is allowed to enter the ordered section, and then only in the order of loop iterations.
In the following example, the code prints out the indexes in sequential order:
!$OMP DO ORDERED,SCHEDULE(DYNAMIC)
!$OMP END ORDERED