Литмир - Электронная Библиотека
Содержание  
A
A

  exit(EXIT_FAILURE);

 }

Обратите внимание на то, что начальное значение семафора равно 0.

В функции

main
, после того как вы запустили новый поток, вы читаете некоторый текст с клавиатуры, загружаете вашу рабочую область и затем наращиваете счетчик семафора с помощью
sem_post
:

 printf("Input some text. Enter 'end' to finish\n");

 while(strncmp("end", work_area, 3) != 0) {

  fgets(work_area, WORK_SIZE, stdin);

  sem_post(&bin_sem);

 }

В новом потоке вы ждете семафор и затем подсчитываете символы ввода:

 sem_wait(&bin_sem);

 while(strncmp("end", work_area, 3) != 0) {

  printf("You input %d characters\n", strlen(work_area)-1);

  sem_wait(&bin_sem);

 }

Пока семафор установлен, вы ждете ввода с клавиатуры. Когда вы получите некоторый ввод, то освобождаете семафор, разрешив второму потоку сосчитать символы перед тем, как первый поток начнет снова считывать ввод с клавиатуры.

И опять потоки совместно используют один и тот же массив

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

Дайте программе отработать:

$ <b>cc -D_REENTRANT thread3.с -о threads -lpthread</b>

$ <b>./thread3</b>

Input some text. Enter 'end', to finish

<b>The Wasp Factory</b>

You input 16 characters

<b>Iain Banks</b>

You input 10 characters

<b>end</b>

Waiting for thread to finish...

Thread joined

В программах с потоками временные ошибки всегда трудно найти, но программа кажется приспособленной и к быстрому вводу текста, и более неспешным паузам.

Как это работает

Когда вы инициализируете семафор, то задаете ему начальное значение, равное 0. Следовательно, когда запускается функция потока, вызов

sem_wait
приостанавливает выполнение и ждет, когда семафор станет ненулевым.

В потоке

main
вы ждете до тех пор, пока у вас не будет некоторого текста, и затем увеличиваете счетчик семафора с помощью функции
sem_post
, которая немедленно разрешает другому потоку вернуться из своей функции
sem_wait
и начать выполнение. После того как он сосчитает символы, поток вновь вызывает
sem_wait
и приостанавливает выполнение до тех пор, пока поток main не вызовет снова
sem_post
для того, чтобы увеличить семафор.

Неочевидные недочеты в разработке, которые заканчиваются в результате неявными ошибками, легко пропустить. Давайте слегка изменим программу на thread3a.c, так чтобы вводимый с клавиатуры текст временами заменялся автоматически формируемым текстом. Замените цикл чтения в

main
следующим:

printf(&quot;Input some text. Enter 'end' to finish\n&quot;);

while (strncmp(&quot;end&quot;, work_area, 3) != 0) {

 if (strncmp(work_area, &quot;FAST&quot;, 4) == 0) {

  sem_post(&amp;bin_sem);

  strcpy(work_area, &quot;Wheeee...&quot;);

 } else {

  fgets(work_area, WORK_SIZE, stdin);

 }

 sem_post(&amp;bin_sem);

}

Теперь, если вы введете

FAST
, программа вызовет
sem_post
, чтобы запустить счетчик символов, но немедленно обновит
work_area
чем-то другим.

$ <b>cc -D_REENTRANT thread3a.с -о thread3a -lpthread</b>

$ <b>./thread3a</b>

Input some text. Enter 'end' to finish

<b>Excession</b>

You input 9 characters

<b>FAST</b>

You input 7 characters

You input 7 characters

You input 7 characters

<b>end</b>

Waiting for thread to finish...

Thread joined

Проблема этой программы заключается в том, что она рассчитывала на то, что ввод текста из программы продлится так долго, что у другого потока хватит времени для подсчета символов до того, как поток

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

Этот пример показывает, как аккуратны вы должны быть с временны́ми условиями в многопоточных программах. Исправить программу можно, применяя дополнительный семафор для того, чтобы заставить поток

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

Синхронизация с помощью мьютексов

Другой способ синхронизации доступа в многопоточных программах — применение мьютексов (сокращение от mutual exclusions — взаимные исключения) или исключающих семафоров, которые разрешают программистам "запирать" объект так, что только один поток может обратиться к нему.

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

220
{"b":"285844","o":1}