THREADPRIVATE

Purpose

The THREADPRIVATE directive allows you to specify named common blocks and named variables as private to a thread but global within that thread. Once you declare a common block or variable THREADPRIVATE, each thread in the team maintains a separate copy of that common block or variable. Data written to a THREADPRIVATE common block or variable remains private to that thread and is not visible to other threads in the team.

In the serial and MASTER sections of a program, only the master thread's copy of the named common block and variable is accessible.

Use the COPYIN clause on the PARALLEL, PARALLEL DO, PARALLEL SECTIONS or PARALLEL WORKSHARE directives to specify that upon entry into a parallel region, data in the master thread's copy of a named common block or named variable is copied to each thread's private copy of that common block or variable.

The THREADPRIVATE directive only takes effect if you specify the -qsmp compiler option.

Syntax

Read syntax diagramSkip visual syntax diagram
>>-THREADPRIVATE--(--threadprivate_entity_list--)--------------><

where threadprivate_entity_list is:

Read syntax diagramSkip visual syntax diagram
   .-,-------------------------.   
   V                           |   
>>---+-variable_name---------+-+-------------------------------><
     '-/ common_block_name /-'     

common_block_name
is the name of a common block to be made private to a thread.
variable_name
is the name of a variable to be made private to a thread.

Rules

You cannot specify a THREADPRIVATE variable, common block, or the variables that comprise that common block in a PRIVATE, FIRSTPRIVATE, LASTPRIVATE, SHARED, or REDUCTION clause.

A THREADPRIVATE variable must have the SAVE attribute. For variables or common blocks declared in the scope of a module, the SAVE attribute is implied. If you declare the variable outside of the scope of the module, the SAVE attribute must be specified.

In THREADPRIVATE directives, you can only specify named variables and named common blocks.

A variable can only appear in a THREADPRIVATE directive in the scope in which it is declared, and a THREADPRIVATE variable or common block may only appear once in a given scope. The variable must not be an element of a common block, or be declared in an EQUIVALENCE statement.

You cannot specify the same common_block_name for both a THREADPRIVATE directive and a THREADLOCAL directive.

All rules and constraints that apply to named common blocks also apply to common blocks declared as THREADPRIVATE. See the COMMON statement .

If you declare a common block as THREADPRIVATE in one scoping unit, you must declare it as THREADPRIVATE in all other scoping units in which it is declared.

If you declare a THREADPRIVATE variable or a THREADPRIVATE common block with the BIND attribute, you must specify the corresponding C entities in a THREADPRIVATE directive in the C program. See Example 4 for the detailed usage information.

On entry into any parallel region, a THREADPRIVATE variable, or a variable in a THREADPRIVATE common block specified in a COPYIN clause is subject to the criteria stated in the Rules section for the COPYIN clause.

On entry into the first parallel region of the program, THREADPRIVATE variables or variables within a THREADPRIVATE common block not specified in a COPYIN clause are subject to the following criteria:
  • If the variable has the ALLOCATABLE attribute, the initial allocation status of each copy of that variable is not currently allocated.
  • If the variable has the POINTER attribute, and that pointer is disassociated through either explicit or default initialization, the association status of each copy of that variable is disassociated. Otherwise, the association status of the pointer is undefined.
  • If the variable has neither the ALLOCATABLE nor the POINTER attribute and is defined through either explicit or default initialization, then each copy of that variable is defined. If the variable is undefined, then each copy of that variable is undefined.
On entry into subsequent parallel regions of the program, THREADPRIVATE variables, or variables within a THREADPRIVATE common block not specified in a COPYIN clause, are subject to the following criteria:
  • If you are using the OMP_DYNAMIC environment variable, or the omp_set_dynamic subroutine to enable dynamic threads and:
    • If the number of threads is smaller than the number of threads in the previous region, and if a THREADPRIVATE object is referenced in both regions, then threads with the same thread number in their respective regions will reference the same copy of that variable.
    • If the number of threads is larger than the number of threads in the previous region, then the definition and association status of a THREADPRIVATE object is undefined, and the allocation status is undefined.
  • If dynamic threads are disabled, the definition, association, or allocation status and definition, if the thread's copy of the variable was defined, is retained.

You cannot access the name of a common block by use association or host association. Thus, a named common block can only appear on a THREADPRIVATE directive if the common block is declared in the scoping unit that contains the THREADPRIVATE directive. However, you can access the variables in the common block by use association or host association. For more information, see Host and Use association .

The -qinit=f90ptr compiler option does not affect pointers that you have declared in a THREADPRIVATE common block.

The DEFAULT clause does not affect variables in THREADPRIVATE common blocks.

Examples

Example 1: In this example, the PARALLEL DO directive invokes multiple threads that call SUB1. The common block BLK in SUB1 shares the data that is specific to the thread with subroutine SUB2, which is called by SUB1.

      PROGRAM TT
        INTEGER :: I, B(50)

!$OMP   PARALLEL DO SCHEDULE(STATIC, 10)
        DO I=1, 50
          CALL SUB1(I, B(I))       ! Multiple threads call SUB1.
        ENDDO
      END PROGRAM TT

      SUBROUTINE SUB1(J, X)
        INTEGER :: J, X, A(100)
        COMMON /BLK/ A
!$OMP   THREADPRIVATE(/BLK/)   ! Array a is private to each thread.
!  ...
        CALL SUB2(J)
        X = A(J) + A(J + 50)
!  ...
      END SUBROUTINE SUB1

      SUBROUTINE SUB2(K)
        INTEGER :: C(100)
        COMMON /BLK/ C
!$OMP   THREADPRIVATE(/BLK/)
!  ...
        C = K
!  ...                       ! Since each thread has its own copy of
                             ! common block BLK, the assignment of
                             ! array C has no effect on the copies of
                             ! that block owned by other threads.
END SUBROUTINE SUB2

Example 2: In this example, each thread has its own copy of the common block ARR in the parallel section. If one thread initializes the common block variable TEMP, the initial value is not visible to other threads.

      PROGRAM ABC
        INTEGER :: I, TEMP(100), ARR1(50), ARR2(50)
        COMMON /ARR/ TEMP
!$OMP   THREADPRIVATE(/ARR/)
        INTERFACE
          SUBROUTINE SUBS(X)
            INTEGER :: X(:)
          END SUBROUTINE
        END INTERFACE
! ...
!$OMP   PARALLEL SECTIONS
!$OMP   SECTION                   ! The thread has its own copy of the
! ...                             ! common block ARR.
          TEMP(1:100:2) = -1
          TEMP(2:100:2) = 2
          CALL SUBS(ARR1)
! ...
!$OMP   SECTION                   ! The thread has its own copy of the
! ...                             ! common block ARR.
          TEMP(1:100:2) = 1
          TEMP(2:100:2) = -2
          CALL SUBS(ARR2)
! ...
!$OMP    END PARALLEL SECTIONS
! ...
        PRINT *, SUM(ARR1), SUM(ARR2)
      END PROGRAM ABC

      SUBROUTINE SUBS(X)
        INTEGER :: K, X(:), TEMP(100)
        COMMON /ARR/ TEMP
!$OMP   THREADPRIVATE(/ARR/)
!  ...
        DO K = 1, UBOUND(X, 1)
          X(K) = TEMP(K) + TEMP(K + 1)    ! The thread is accessing its
                                          ! own copy of
                                          ! the common block.
        ENDDO
! ...
      END SUBROUTINE SUBS

The expected output for this program is:

50 -50

Example 3: In the following example, local variables outside of a common block are declared as THREADPRIVATE.

      MODULE MDL
        INTEGER          :: A(2)
        INTEGER, POINTER :: P
        INTEGER, TARGET  :: T
!$OMP THREADPRIVATE(A, P)
      END MODULE MDL


      PROGRAM MVAR
      USE OMP_LIB
      USE MDL

      INTEGER :: I

      CALL OMP_SET_NUM_THREADS(2)
      A = (/1, 2/)
      T = 4
      P => T

!$OMP PARALLEL PRIVATE(I) COPYIN(A, P)
      I = OMP_GET_THREAD_NUM()
      IF (I .EQ. 0) THEN
        A(1) = 100
        T = 5
      ELSE IF (I .EQ. 1) THEN
        A(2) = 200
      END IF
!$OMP END PARALLEL

!$OMP PARALLEL PRIVATE(I)
      I = OMP_GET_THREAD_NUM()
      IF (I .EQ. 0) THEN
        PRINT *, 'A(2) = ', A(2)
      ELSE IF (I .EQ. 1) THEN
        PRINT *, 'A(1) = ', A(1)
        PRINT *, 'P => ', P
      END IF
!$OMP END PARALLEL

      END PROGRAM MVAR
If dynamic threads mechanism is disabled, the expected output is:
A(2) = 2
A(1) = 1
P => 5
or
A(1) = 1
P => 5
A(2) = 2

Example 4: In this example, the C interoperable variable NUMVAR is declared as THREADPRIVATE. Changes made by thread 1 to the private copy of NUMVAR does not affect the copy of the master thread.

Fortran source file
      MODULE M
        USE, INTRINSIC :: ISO_C_BINDING
        INTEGER(C_INT), BIND(C) :: NUMVAR(10)
!$OMP THREADPRIVATE(NUMVAR)
      END MODULE M
      
      PROGRAM P
        USE M
        USE OMP_LIB
        INTERFACE
          SUBROUTINE INIT_NUM() BIND(C)
          END SUBROUTINE INIT_NUM 
          
          SUBROUTINE PRINT_NUM() BIND(C)
          END SUBROUTINE PRINT_NUM 
        END INTERFACE
        INTEGER TNUM
        CALL INIT_NUM()
        
        CALL OMP_SET_NUM_THREADS(2)
        
!$OMP PARALLEL COPYIN(NUMVAR)
        TNUM = OMP_GET_THREAD_NUM()
        IF (TNUM .EQ. 0) THEN
          ! PROCESS NUMVAR
        ELSE IF (TNUM .EQ. 1) THEN
          NUMVAR = NUMVAR * 4
          CALL PRINT_NUM()
          ! PROCESS NUMVAR
        END IF
!$OMP END PARALLEL
        CALL PRINT_NUM()
      END PROGRAM P
C source file
#include <stdio.h>
extern int numvar[10];
#pragma omp threadprivate(numvar)

void init_num(){ 
  for (int i = 0; i < 10; ++ i)    
    numvar[i] = i * i;
}

void print_num(){
  for (int i = 0; i < 10; ++ i)    
    printf("%d ", numvar[i]);
  printf("\n");
}
The expected output for this program is:
0 4 16 36 64 100 144 196 256 324
0 1 4 9 16 25 36 49 64 81


Voice your opinion on getting help information Ask IBM compiler experts a technical question in the IBM XL compilers forum Reach out to us