Главная страница Взаимодействие нетривиальных процессов 18 } nready = { 19 PTHREAD MUTEX INITIALIZER. PTHREAD COND INITIALIZER 20 }; Функции produce и consume претерпевают некоторые изменения. Их текст дан в листинге 7.6. Листинг 7.6. Функции produce и consume mutex/prodconse.c 46 void * 47 produce(void *arg) 48 { 49 for ( : : ) { 50 Pthread niutexJ ock (&put. mutex): 51 if (put.nput >= nitems) { 52 Pthread mutex unlock(&put.mutex): 53 return(NULL); /* массив заполнен, готово */ 54 } 55 buff[put.nput] = put.nval: 56 put.nput++: 57 put.nval++: 58 Pthread mutex unlock(&put.mutex): 59 Pthread mutexJock(&nready.mutex): 60 if (nready.nready == 0) 61 Pthread cond signal(&nready.cond): 62 nready.nready++: 63 Pthread mutex unlock(&nready.mutex): 64 *((int *) arg) += 1: 65 } 66 } 67 void * 68 consume(void *arg) 69 { 70 int i: 71 for (i = 0; i < nitems: i++) { 72 Pthread mutexJock(&nready.mutex): 73 while (nready.nready 0) 74 Pthread cond wait(&nready.cond. &nready.mutex): 75 nready.nready--: 76 Pthread mutex unlock(&nready.mutex): 77 if (buff[i] != i) 78 printf( buff[d] = d\n . i. buff[i]): 79 } 80 return(NULL); 81 } Помещение очередного элемента в массив 50-58 Для блокирования критической области в потоке-производителе теперь используется исключение put. mutex. Уведомление потребителя 59-64 Мы увеличиваем счетчик nready.nready, в котором хранится количество элементов, готовых для обработки потребителем. Перед его увеличением мы проверяем, не было ли значение счетчика нулевым, и если да, то вызывается функция pthread cond si gnal, позволяющая возобновить выполнение всех потоков (в данном случае потребителя), ожидающих установки ненулевого значения этой переменной. Теперь мы видим, как взаимодействуют взаимное исключение и связанная с ним условная переменная. Счетчик используется совместно потребителем и производителями, поэтому доступ к нему осуществляется с блокировкой соответствующего взаимного исключения (nready .mutex). Условная переменная используется для ожидания и передачи сигнала. Потребитель ждет, пока значение nready.nready не станет ОТЛИЧНЫМ от нуля 72-76 Потребитель просто ждет, пока значение счетчика nready .nready не станет отличным от нуля. Поскольку этот счетчик используется совместно с производителями, его значение можно проверять только при блокировке соответствующего взаимного исключения. Если при проверке значение оказывается нулевым, мы вызываем pthread cond wait для приостановки процесса. При этом выполняются два атомарных действия: 1. Разблокируется nready.mutex. 2. Выполнение потока приостанавливается, пока какой-нибудь другой поток не вызовет pthread cond signal для этой условной переменной. Перед возвращением управления потоку функция pthread cond wait блокирует nready .mutex. Таким образом, если после возвращения из функции мы обнаруживаем, что счетчик имеет ненулевое значение, мы уменьшаем этот счетчик (зная, что взаимное исключение заблокировано) и разблокируем взаимное исключение. Обратите внимание, что после возвращения из pthread cond wait мы всегда заново проверяем условие, поскольку может произойти ложное пробуждение при отсутствии выполнения условия. Различные реализации стремятся уменьшить количество ложных пробуждений, но они все равно происходят. Код, передающий сигнал условной переменной, выглядит следующим образом: struct { pthreacl niutex t mutex: pthreacl concl t cond; переменные, для которых устанавливается условие } var - { PTHREAD MUTEX INITIALIZER. PTHREAD COND INITIALIZER. ... }: Pth read mutex lock(&var.mutex): установка истинного значения условия Pthread cond signal(&var.cond): Pthread mutex unlock(&var.mutex): в нашем примере переменная, для которой устанавливалось условие, представляла собой целочисленный счетчик, а установка условия означала просто увеличение счетчика. Мы оптимизировали программу, посылая сигнал только при изменении значения счетчика с О на 1. Код, проверяющий условие и приостанавливающий процесс, если оно не выполняется, обычно выглядит следующим образом: Pthreacl niutex l ock (&va г. mutex): while (условие ложно) Рth reacl concl wa i t (&va г. cond, &va г. mutex); изменение условия Pthread mutex unlock(&var.mutex); Исключение конфликтов блокировок в приведенном выше фрагменте кода, как и в листинге 7.6, функция pthread cond signal вызывалась потоком, блокировавшим взаимное исключение, относящееся к условной переменной, для которой отправлялся сигнал. Мы можем представить себе, что в худшем варианте система, немедленно передаст управление потоку, которому направляется сигнал, и он начнет выполняться и немедленно остановится, поскольку не сможет заблокировать взаимное исключение. Альтернативный код, помогающий этого избежать, для листинга 7.6 будет иметь следующий вид: int dosignal: Pthread mutex lock(nready.mutex): dosignal = (nready.nready == 0); nready.nready++: Pthread mutex unlock(&n ready.mutex); if (dosignal) Pthread cond signal(&nready.cond); Здесь мы отправляем сигнал условной переменной только после разблокирования взаимного исключения. Это разрешено стандартом Posix: поток, вызывающий pthreajd cond signal, не обязательно должен в этот момент блокировать связанное с переменной взаимное исключение. Однако Posix говорит, что если требуется предсказуемое поведение при одновременном выполнении потоков, это взаимное исключение должно быть заблокировано процессом, вызывающим pthread cond signal. 7.6. Условные переменные: время ожидания и широковещательная передача в обычной ситуации pthread cond signal запускает выполнение одного потока, ожидающего сигнал по соответствующей условной переменной. В некоторых случаях поток знает, что требуется пробудить несколько других процессов. Тогда можно воспользоваться функцией pthread cond broadcast для пробуждения всех процессов, заблокированных в ожидании сигнала данной условной переменной.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |