Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [ 58 ] 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

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 для пробуждения всех процессов, заблокированных в ожидании сигнала данной условной переменной.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [ 58 ] 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.