Вперед: 3.4.3. Функции для работы с системным таймером
Назад: 3.4.1. Подпрограммы для работы с переменными окружения
К содержанию: Оглавление


3.4.2. Подпрограммы синхронизации с использованием механизма замков

В OpenMP, помимо директивы синхронизации BARRIER, имеется набор встроенных подпрограмм и функций для синхронизации нитей с помощью механизма замков. Различают два типа замков - одинарные и множественные. Одинарный замок может быть захвачен одной нитью только один раз, при этом он переходит в состояние заблокированный и может быть разблокирован только этой же нитью. Множественные замки могут захватываться нитью многократно. Для этого вводится понятие коэффициента <захваченности> (nested count), который при каждом захвате увеличивается на 1. Множественный замок считается разблокированным, когда его коэффициент <захваченности> равен 0.

OMP_INIT_LOCK - подпрограмма инициализации замка.

C/C++:

#include <omp.h>
void omp_init_lock(omp_lock_t *lock)
void omp_init_nest_lock(omp_nest_lock_t *lock)

Fortran:

SUBROUTINE OMP_INIT_LOCK(var)
SUBROUTINE OMP_INIT_NEST_LOCK(var)

В языке Фортран переменная var должна быть целого типа, достаточной длины для покрытия всего адресного пространства программы, т.е. на 64-х битных системах следует использовать тип INTEGER*8. Функция создает новый замок, устанавливая его в состояние <разблокированный>. Для множественного замка коэффициент <захваченности> устанавливается в 0.

OMP_DESTROY_LOCK - подпрограмма деинициализации замка.

C/C++:

#include <omp.h>
void omp_destroy_lock(omp_lock_t *lock)
void omp_destroy_nest_lock(omp_nest_lock_t *lock)

Fortran:

SUBROUTINE OMP_DESTROY_LOCK(var)
SUBROUTINE OMP_DESTROY_NEST_LOCK(var)

OMP_SET_LOCK - подпрограмма захвата замка.

C/C++:

#include <omp.h>
void omp_set_lock(omp_lock_t *lock)
void omp_set_nest_lock(omp_nest_lock_t *lock)

Fortran:

SUBROUTINE OMP_SET_LOCK(var)
SUBROUTINE OMP_SET_NEST_LOCK(var)

Нить, вызвавшая эту функцию, дожидается освобождения замка, затем захватывает его. Если множественный замок уже захвачен данной нитью, то нить не блокируется, а увеличивается на единицу коэффициент <захваченности>.

OMP_UNSET_LOCK - подпрограмма освобождения замка.

C/C++:

#include <omp.h>
void omp_unset_lock(omp_lock_t *lock)
void omp_unset_nest_lock(omp_nest_lock_t *lock)

Fortran:

SUBROUTINE OMP_UNSET_LOCK(var)
SUBROUTINE OMP_UNSET_NEST_LOCK(var)

Замок может быть освобожден только захватившей его нитью. Если множественный замок был захвачен данной нитью, то уменьшается на единицу коэффициент <захваченности>.

OMP_TEST_LOCK - неблокирующая функция захвата замка.

C/C++:

#include <omp.h>
void omp_test_lock(omp_lock_t *lock)
void omp_test_nest_lock(omp_nest_lock_t *lock)

Fortran:

LOGICAL FUNCTION OMP_TEST_LOCK(var)
LOGICAL FUNCTION OMP_TEST_NEST_LOCK(var)

Нить, вызвавшая эту функцию, пытается захватить замок, но не блокируется, если это не возможно.

В заключение рассмотрения функций работы с замками приведем пример простой программы на использование этих функций.

Программа lock.c

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

omp_lock_t simple_lock;         
          
int main() {
  omp_init_lock(&simple_lock);
          
  #pragma omp parallel num_threads(4)
  {
   int tid = omp_get_thread_num();

   while (!omp_test_lock(&simple_lock))
   printf("Thread %d - failed to acquire simple_lock\n",
           tid);          
   printf("Thread %d - acquired simple_lock\n", tid);
            
   printf("Thread %d - released simple_lock\n", tid);
   omp_unset_lock(&simple_lock);
  }          
  omp_destroy_lock(&simple_lock);
}

Результат работы:

Thread 2 - acquired simple_lock
Thread 2 - released simple_lock
Thread 0 - failed to acquire simple_lock
Thread 0 - acquired simple_lock
Thread 0 - released simple_lock
Thread 1 - failed to acquire simple_lock
Thread 1 - acquired simple_lock
Thread 1 - released simple_lock
Thread 3 - failed to acquire simple_lock
Thread 3 - acquired simple_lock
Thread 3 - released simple_lock

Программа lock.f

program lockf
   include "omp_lib.h"
   integer (kind=omp_lock_kind) lock
   INTEGER tid
   
   CALL omp_init_lock(lock)
!$OMP PARALLEL NUM_THREADS(4) PRIVATE(tid)
   tid = omp_get_thread_num()
   DO WHILE(.NOT. omp_test_lock(lock)) 
   print *, 'Thread =', tid, 'failed to set lock'
   END DO
   print *, 'Thread =', tid, 'set lock'
   CALL omp_unset_lock(lock) 
   print *, 'Thread =', tid, 'released lock'
!$OMP END PARALLEL   
   CALL omp_destroy_lock(lock)
   END

Результат работы:

 Thread =      3 set lock
 Thread =      3 released lock
 Thread =      0 failed to set lock
 Thread =      0 set lock
 Thread =      0 released lock
 Thread =      1 failed to set lock
 Thread =      1 set lock
 Thread =      1 released lock
 Thread =      2 failed to set lock
 Thread =      2 set lock
 Thread =      2 released lock



Вперед: 3.4.3. Функции для работы с системным таймером
Назад: 3.4.1. Подпрограммы для работы с переменными окружения
К содержанию: Оглавление