Чтобы исключить возможность гонки, необходимо сделать операции
job_queue
и удаление задания должны выполняться как одна атомарная операция.4.4.2. Исключающие семафоры
Решение проблемы гонки заключается в том, чтобы позволить только одному потоку обращаться к очереди в конкретный момент времени. Когда поток начинает просматривать очередь, все остальные потоки вынуждены дожидаться, пока он удалит очередное задание из списка.
Реализация такого решения требует поддержки от операционной системы. В Linux имеется специальное средство, называемое
Чтобы создать исключающий семафор, нужно объявить переменную типа pthread_mutex_t
pthread_mutex_init()
. Вторым аргументом этой функции является указатель на объект атрибутов семафора. Как и в случае функции pthread_create()
, если объект атрибутов пуст, используются атрибуты по умолчанию. Переменная исключающего семафора инициализируется только один раз. Вот как это делается:pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
Более простой способ создания исключающего семафора со стандартными атрибутами — присвоение переменной специального значения PTHREAD_MUTEX_INITIALIZER
pthread_mutex_init()
в таком случае не требуется. Это особенно удобно для глобальных переменных (а в C++ — статических переменных класса). Предыдущий фрагмент программы эквивалентен следующей записи:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Поток может попытаться захватить исключающий семафор, вызвав функцию pthread_mutex_lock()
pthread_mutex_lock()
блокируется и возобновляется только тогда, когда семафор вновь становится свободным. Сразу несколько потоков могут ожидать освобождения исключающего семафора. Когда это событие наступает, только один поток (выбираемый произвольным образом) разблокируется и получает возможность захватить семафор; остальные потоки остаются заблокированными.Функция pthread_mutex_unlock()
В листинге 4.11 представлена другая версия программы, работающей с очередью заданий. Теперь очередь "защищена" исключающим семафором. Прежде чем получить доступ к очереди (для чтения или записи), каждый поток сначала захватывает семафор. Только когда вся последовательность операций проверки очереди и удаления задания из нее будет закончена, произойдет освобождение семафора. Благодаря этому не возникает описанное выше состояние гонки.
#include
#include
struct job {
/* Ссылка на следующий элемент связанного списка. */
struct job* next;
/* Другие поля, описывающие требуемую операцию... */
};
/* Список отложенных заданий. */
struct job* job_queue;
/* Исключающий семафор, защищающий очередь. */
pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Обработка заданий до тех пор, пока очередь не опустеет. */
void* thread_function(void* arg) {
while (1) {
struct job* next_job;
/* Захват семафора, защищающего очередь. */
pthread_mutex_lock(&job_queue_mutex);