Книга: Программирование для Linux. Профессиональный подход

Листинг 4.11. (job-queue2.c) Работа с очередью заданий, защищенной исключающим семафором

Листинг 4.11. (job-queue2.c) Работа с очередью заданий, защищенной исключающим семафором

#include <malloc.h>
#include <pthread.h>
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);
  /* Теперь можно проверить, является ли очередь пустой. */
  if (job_queue == NULL)
   next_job = NULL;
  else {
   /* Запрашиваем следующее задание. */
   next_job = job_queue;
   /* Удаляем задание из списка. */
   job_queue = job_queue->next;
  }
  /* Освобождаем семафор, так как работа с очередью окончена. */
  pthread_mutex_unlock(&job_queue_mutex);
  /* Если очередь пуста, завершаем поток. */
  if (next_job == NULL)
   break;
  /* Выполняем задание. */
  process_job(next_job);
  /* Очистка. */
  free(next_job);
 }
 return NULL;
}

Все операции доступа к совместно используемому указателю job_queue происходят между вызовами функций pthread_mutex_lock() и pthread_mutex_unlock(). Объект задания, ссылка на который хранится в переменной next_job, обрабатывается только после того, как ссылка на него удаляется из очереди, что позволяет обезопасить этот объект от других потоков.

Обратите внимание на то, что, если очередь пуста (т.е. указатель job_queue равен NULL), цикл не завершается немедленно. Это привело бы к тому, что исключающий семафор так и остался бы в захваченном состоянии и не позволил бы ни одному другому потоку получить доступ к очереди заданий. Мы действуем иначе: записываем в переменную next_job значение NULL и выходим из цикла только после освобождения семафора.

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

void enqueue_job(struct job* new_job) {
 pthread_mutex_lock(&job_queue_mutex);
 new_job->next = job_queue;
 job_queue = new_job;
 pthread_mutex_unlock(&job_queue_mutex);
}

Оглавление книги


Генерация: 1.382. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз