void initialize_flag() {

 /* Инициализация исключающего семафора и сигнальной

    переменной. */

 pthread_mutex_init(&thread_flag_mutex, NULL);

 pthread_cond_init(&thread_flag_cv, NULL);

 /* Инициализация флага. */

 thread_flag = 0;

}

/* Если флаг установлен, многократно вызывается функция

   do_work(). В противном случае поток блокируется. */

void* thread_function(void* thread_arg) {

 /* Бесконечный цикл. */

 while (1) {

  /* Захватываем исключающий семафор, прежде чем обращаться

     к флагу. */

  pthread_mutex_lock(&thread_flag_mutex);

  while (!thread_flag)

   /* Флаг сброшен. Ожидаем сигнала об изменении условной

      переменной, указывающего на то, что флаг установлен.

      При поступлении сигнала поток разблокируется и снова

      проверяет флаг. */

   pthread_cond_wait(&thread_flag_cv, &thread_flag_mutex);

  /* При выходе из цикла освобождаем исключающий семафор. */

  pthread_mutex_unlock(&thread_flag_mutex);

  /* Выполняем требуемые действия. */

  do_work();

 }

 return NULL;

}

/* Задаем значение флага равным FLAG_VALUE. */

void set_thread_flag(int flag_value) {

 /* Захватываем исключающий семафор, прежде чем изменять

    значение флага. */

 pthread_mutex_lock(&thread_flag_mutex);

 /* Устанавливаем флаг и посылаем сигнал функции

    thread_function(), заблокированной в ожидании флага.

    Правда, функция не сможет проверить флаг, пока

    исключающий семафор не будет освобожден. */

 thread_flag = flag_value;

 pthread_cond_signal(&thread_flag_cv);

 /* освобождаем исключающий семафор. */

 pthread_mutex_unlock(&thread_flag_mutex);

}

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

Сигнальная переменная может вообще не быть связана ни с каким условием, а служить лишь средством блокирования потока до тех пор, пока какой-нибудь другой поток не "разбудит" его. Для этой же цели может использоваться и семафор. Принципиальная разница между ними заключается в том, что семафор "запоминает" сигнал, даже если ни один поток в это время не был заблокирован, а сигнальная переменная регистрирует сигнал только в том случае, если его ожидает какой-то поток. Кроме того, семафор всегда разблокирует лишь один поток, тогда как с помощью функции pthread_cond_broadcast() можно разблокировать произвольное число потоков.

<p>4.4.7. Взаимоблокировки двух и более потоков</p>

Взаимоблокировка происходит, когда два (или более) потока блокируются в ожидании события, наступление которого на самом деле зависит от действий одного из заблокированных потоков. Например, если поток A ожидает изменения сигнальной переменной, устанавливаемой в потоке Б, а поток Б, в свою очередь, ждет сигнала от потока А, возникает тупиковая ситуация. Ни один из потоков никогда не пошлет сигнал другому. Необходимо тщательно избегать таких ситуаций, потому что их очень трудно обнаруживать.

Перейти на страницу:

Похожие книги