Условные переменные
Условная переменная (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; просмотров: 3006;