Вход/Регистрация
Программирование для Linux. Профессиональный подход
вернуться

Самьюэл Алекс

Шрифт:

do_work;

/* Если флаг не установлен, ничего не делаем. Просто переходим

на следующую итерацию цикла. */

 }

 return NULL;

}

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

void set_thread_flag(int flag_value) {

 /* Защищаем флаг с помощью исключающего семафора. */

 pthread_mutex_lock(&thread_flag_mutex);

 thread_flag = flag_value;

 pthread_mutex_unlock(&thread_flag_mutex);

}

Сигнальная переменная позволяет организовать такую проверку, при которой поток либо выполняется, либо блокируется. Как и в случае семафора, поток может ожидать сигнальную переменную. Поток A, находящийся в режиме ожидания, блокируется до тех пор, пока другой поток. Б, не просигнализирует об изменении состояния этой переменной. Сигнальная переменная не имеет внутреннего счетчика, что отличает ее от семафора. Поток А должен перейти в состояние ожидания до того, как поток Б пошлет сигнал. Если сигнал будет послал раньше, он окажется потерянным и поток А заблокируется, пока какой-нибудь поток не пошлет сигнал еще раз.

Вот как можно сделать предыдущую программу более эффективной.

■ Функция

thread_function
в цикле проверяет флаг. Если он не установлен, поток переходит в режим ожидания сигнальной переменной.

■ Функция

set_thread_flag
устанавливает флаг и сигнализирует об изменении условной переменной. Если функция
thread_function
была заблокирована в ожидании сигнала, она разблокируется и снова проверяет флаг.

Но существует одна проблема: возникает гонка между операцией проверки флага и операцией сигнализирования или ожидания сигнала. Предположим, что функция

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

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

■ В цикле необходимо захватить исключающий семафор и прочитать значение флага.

■ Если флаг установлен, нужно разблокировать семафор и выполнить требуемые действия.

■ Если флаг не установлен, одновременно выполняются операции освобождения семафора и перехода в режим ожидания сигнала.

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

Сигнальная переменная имеет тип

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

■ Функция

pthread_cond_init
инициализирует сигнальную переменную. Первый ее аргумент — это указатель на объект типа
pthread_cond_t
. Второй аргумент (указатель на объект атрибутов сигнальной переменной) игнорируется в Linux. Исключающий семафор должен инициализироваться отдельно, как описывалось в разделе 4.4.2, "Исключающие семафоры".

■ Функция

pthread_cond_signal
сигнализирует об изменении переменной. При этом разблокируется один из потоков, находящийся в ожидании сигнала. Если таких потоков нет, сигнал игнорируется. Аргументом функции является указатель на объект типа
pthread_cond_t
.

Похожая функция

pthread_cond_broadcast
разблокирует все потоки, ожидающие данного сигнала.

■ Функция

pthread_cond_wait
блокирует вызывающий ее поток до тех пор, пока не будет получен сигнал об изменении заданной переменной. Первым ее аргументом является указатель на объект типа
pthread_cond_t
. Второй аргумент — это указатель на объект исключающего семафора (тип
pthread_mutex_t
).

В момент вызова функции

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

Перечисленные ниже этапы должны выполняться всякий раз, когда программа тем или иным способом меняет результат проверки условия, контролируемого сигнальной переменной (в нашей программе условие — это значение флага):

1. Захватить исключающий семафор, дополняющий сигнальную переменную.

2. Выполнить действие, включающее изменение результата проверки условия (в нашем случае — установить флаг).

3. Послать сигнал (возможно, широковещательный) об изменении условия.

4. Освободить исключающий семафор.

  • Читать дальше
  • 1
  • ...
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: