Условные переменные

Условная переменная (condvar — сокращение от condition variable) используется для блокировки потока по какому-либо условию во время выполнения критической секции кода. Условие может быть сколь угодно сложным и не зависит от условной переменной. Однако условная переменная всегда должна использоваться совместно с мьютексом для проверки условия.

Условные переменные поддерживают следующие функции:

· ожидание условной переменной (wait) (pthread_cond_wait());

· единичная разблокировка потока (signal) (pthread_cond_signal())

· множественная разблокировка потока (broadcast) (pthread_cond_broadcast()),

Приведем пример типичного использования условной переменной:

pthread_mutex_lock ( &m ) ;-…

while (!arbitrary condition) {

pthread_cond_wait( &cv, &m );

}

pthread_mutex_unlock ( &m ) ;

 

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

Цикл while в приведенном примере требуется по двум причинам. Во-первых, стандарты posix не гарантируют отсутствие ложных пробуждений (например, в многопроцессорных системах). Во-вторых, если другой поток изменяет условие, необходимо заново выполнить его проверку, чтобы убедиться, что изменение соответствует принятым критериям. При блокировании ожидающего потока связанный с условной переменной мутекс атомарно освобождается функцией pthread_cond_wait() для того, чтобы другой поток мог войти в критическую секцию программного кода.

Поток, который выполняет единичную разблокировку потока, разблокирует поток с наивысшим приоритетом, который стоит в очереди на условной переменной. Операция множественной разблокировки потока разблокирует все потоки, стоящие в очереди на условной переменной. Связанный с условной переменной мутекс освобождается атомарно разблокированным потоком с наивысшим приоритетом. После обработки критической секции кода этот поток должен освободить мутекс.

Другой вид операции ожидания условной переменной (pthread__cond_timedwair()) позволяет установить таймаут. По окончании этого периода ожидающий поток может быть разблокирован.

 

Барьеры

Барьер — это механизм синхронизации, который позволяет скоординировать работу нескольких взаимодействующих потоков таким образом, чтобы каждый из них остановился в заданной точке в ожидании остальных потоков, прежде чем продолжить свою работу.

В отличие от функции pthreadjoin(), при которой поток ожидает завершения другого потока, барьер заставляет потоки встретиться в определенной точке. После того как заданное количество потоков достигает установленного барьера, все эти потоки разблокируются и продолжат свою работу. Барьер создается с помощью функции pthread_barrier_init():

#include <pthread.h>

int

pthread_barrier_init (pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count);

В результате выполнения этого кода создается барьер по заданному адресу (указатель на барьер находится в аргументе barrier) и с атрибутами, установ­ленными аргументом attr. Аргумент count задает количество потоков, кото­рые должны вызвать функцию pthread_barrier_wait().

После создания барьера каждый поток вызывает функцию pthread_barrier_wait (), тем самым сообщая о завершения этого действия:

#include <pthread.h>

int pthread_barrier_wait (pthread_barrier_t "barrier};

Когда поток вызывает функцию pthread_barrier_wait(), он блокируется до тех пор, пока то число потоков, которое было задано функцией pthread_barrier_init(), не вызовет функцию pthread_jbarrier_wait() и, соответст­венно, не заблокируется. После того как заданное количество потоков вы­зовет функцию pthread_barrier_wait(), все они разблокируются одновременно.

/*

* barrierl.с

*/

#include <stdio.h>

#include <time.h>

#include <pthread.h>

#include <sys/neutrino.h>

pthread_barrier_t barrier; // объект синхронизации типа "барьер"

main () // игнорировать аргументы

{

time_t now;// создать барьер со значением счетчика 3

pthread_barrier_init (&barrier, NULL, 3); // стартовать два потока - threadl и thread2

pthread_create (NOLL, NOLL, threadl, NULL); // потоки threadl и thread2 выполняются

pthread_create (NDLL, NDLL, thread2, NDLL);// ожидание завершения

time (&now);

printf ("main() waiting for barrier at %s", ctime (&now));

pthread_barrier_wait (&barrier);// после этого момента все три потока завершены

time (&now);

printf ("barrier in mainO done at %s", ctime (&now));

}

void *

threadl (void *not used)

{

time_t now;

time (&now); // выполнение вычислений

printf ("threadl starting at %s", ctime (&now));// пауза

sleep (20);

pthread_barrier_wait (&barrier) ;// после этого момента все три потока завершены

time (&now);

printf ("barrier in threadl{) done at %s", ctime (&now) ) ;

}

void *

thread2 (void *not__used)

{

time_t now,-

time (&now) ; // выполнение вычислений

printf ("thread2 starting at %s", ctime (&now));// пауза

sleep {40);

pthread_barrier_wait (&barrier) ;

// после этого момента все три потока завершены

time (&now);

printf {"barrier in thread2() done at %s", ctime (&now));

}

В примере из листинга основной поток создает барьер, после запуска которого начинается подсчет количества потоков, заблокированных на барьере для синхронизации. В данном случае количество синхронизируемых потоков задается равным 3: поток main(), поток thread1() и поток thread2().

 

Запускаются потоки thread1() и thread2(). Для наглядности в потоке задается пауза, чтобы имитировать процесс вычислений. Для выполнения синхрони­зации основной поток блокируется на барьере и ожидает разблокировки, которая произойдет после того, как остальные два потока не присоединятся к нему на этом барьере.

 

Функция Описание
pthread_barrirattr_getpshared() Получить значение атрибута совмест­ного- использования для заданного барьера
pthread_barrierattr_destroy() Уничтожить атрибутную запись барь­ера
pthread_barrierattr_init() Инициализировать атрибуты объекта
pthread_barrierattr_setpshared() Установить значение атрибута совме­стного использования для заданного барьера
pthread_barrier_destroy() Уничтожить барьер
pthread_barrier_init() Инициализировать барьер
pthread_barrier_ wait() Синхронизировать потоки на барьере

 

Ждущие блокировки

Ждущие блокировки (sleepon locks) работают аналогично условным пере­менным, за исключением некоторых деталей. Как и условные переменные, ждущие блокировки (pthread_sleepon_lock()) могут использоваться для бло­кировки потока до тех пор, пока условие не станет истинным (аналогично изменению значения ячейки памяти). Но в отличие от условных перемен­ных (которые должны существовать для каждого проверяемого условия), ждущие блокировки применяются к одному мm.тексу и динамически созда­ваемой условной переменной независимо от количества проверяемых усло­вий. Максимальное число условных переменных в конечном итоге равно максимальному числу блокированных потоков.

 








Дата добавления: 2017-01-29; просмотров: 2938;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.011 сек.