Race Conditions and Criticality

Consider this C program fragment to compute $pi$ using the rectangle rule:

double area, pi, x;
int i, n;
...
area = 0.0;
for (i = 0; i < n; i++) {
    x = (i+0.5/n);
    area += 4.0/(1.0 + x*x);
}
pi = area/n;

If we naively parallelize the loop

double area, pi, x;
int i, n;
...
area = 0.0;
#pragma omp parallel for private(x)
for (int i=0; i<n; i++) {
    x = (i+0.5/n);
    area += 4.0/(1.0 + x*x);
}
pi = area/n;

we set up a race condition in which one process may “race ahead” of another and not see its change to shared variable area.

:

Race condition due to uncontrolled access to main memory by multiple threads.
Two threads compute area without coordination. The correct result should be 18.995

We can illustrate what is happening by considering a timeline. We will limit ourselves to two threads for the purpose of illustration. Both read a value from main memory, then increment according to where they are in their individual loops. Thread 0 is faster than Thread 1 and writes its result to main memory. Thread 1 finishes its update and writes its value of area back to main memory without using the value updated by Thread 0.

We cannot make area a private variable, because at most one value of a private variable will be written back to memory (with lastprivate when the parallel region terminates. Private variables are all temporary and do not persist beyond the parallel region. The race condition means that we are very unlikely to obtain a correct result.

Race condition timeline.
Race Condition Timeline.

Critical Sections

A critical section is a portion of a parallel region that only one thread at a time may execute. We denote a critical section with the critical directive.

#pragma omp critical

in front of a block of C.

For Fortran the equivalent is

$omp critical
$end omp critical

Correcting the Pi Calculation

We will enclose the update to area inside a critical section

double area, pi, x;
int i, n;
...
area = 0.0;
#pragma omp parallel for private(x)
for (int i = 0; i < n; i++) {
    x = (i+0.5)/n;
#pragma omp critical
    area += 4.0/(1.0 + x*x);
}
pi = area / n;

This solves the problem and returns the correct result, but at the cost of efficiency. Only one thread at a time may execute the statement; i.e., it is sequential code. And the time to execute this statement is a significant part of loop.Consequently, our parallel speedup will be severely constrained by this serialization.

Critical sections cause serialization, but if used judiciously they can avoid race conditions without seriously impacting efficiency.

Exercise

Download the full code for your language. Compile (for C/C++/Fortran) and run with the race condition. Uncomment the critical directive and run it again.

C

Contents of omp_critical_area.c

#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main() {

    double area, pi, x;
    int n;

    n=1000;

    area=0.0;
#pragma omp parallel for private(x) 
    for (int i=0; i< n; i++) {
        x=(i+0.5)/n;
// #pragma omp critical
        area+=4.0/(1.0+x*x);
    }

    pi=area/n;
    printf("Pi is %f\n",pi);

    return(0);
}

Download omp_critical_area.c file

Fortran

Contents of omp_critical_area.f90

program pie
use omp_lib
implicit none

double precision :: area, pi, x
integer          :: i, n
integer          :: nthreads

   n=10000
   area=0.0

!$omp parallel do private(x) 
    do i=1,n
       x=(i+0.5d0)/n
       !! !$omp critical 
       area=area+4.0/(1.0d0+x**2)
       !! !$omp end critical 
    enddo
!$omp end parallel do

    pi=area/n
    write(*,'(a,f9.6)') "Pi is ", pi

end program

Download omp_critical_area.f90 file

Python

Contents of omp_critical_area.py

import os
from omp4py import *

@omp
def pie(nthreads):
    omp_set_num_threads(nthreads)
    n=1000
    area=0.0
    x=0.0
    with omp("parallel for private(x)"):
        for i in range(n):
            x=(i+0.5)/n;
            #with omp("critical"):
            area+=4.0/(1.0+x*x);

    pi=area/n;
    return pi

nthreads=os.cpu_count()
pi=pie(nthreads)
print(f"Pi is {pi:.6f}")

Download omp_critical_area.py file

Run the codes for your language of choice. For C and Fortran, also compile without OpenMP to run as a serial code and compare the results.

Previous
Next
© 2026 The Rector and Visitors of the University of Virginia