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

Листинг 4.10. (job-queue1.c) Потоковая функция, работающая с очередью заданий

закрыть рекламу

Листинг 4.10. (job-queue1.c) Потоковая функция, работающая с очередью заданий

#include <malloc.h>
struct job {
 /* Ссылка на следующий элемент связанного списка. */
 struct job* next;
 /* Другие поля, описывающие требуемую операцию... */
};
/* Список отложенных заданий. */
struct job* job_queue;
/* Обработка заданий до тех пор, пока очередь не опустеет. */
void* thread_function(void* arg) {
 while (job_queue != NULL) {
  /* Запрашиваем следующее задание. */
  struct job* next_job = job_queue;
  /* Удаляем задание из списка. */
  job_queue = job_queue->next;
  /* выполняем задание. */
  process_job(next_job);
  /* Очистка. */
  free(next_job);
 }
 return NULL;
}

Теперь предположим, что два потока завершают свои операции примерно в одно и то же время, а в очереди остается только одно задание. Первый поток проверяет, равен ли указатель job_queue значению NULL, и, обнаружив, что очередь не пуста, входит в цикл, где сохраняет указатель на объект задания в переменной next_job. В этот момент Linux прерывает первый поток и активизирует второй. Он тоже проверяет указатель job_queue, устанавливает, что он не равен NULL, и записывает тот же самый указатель в свою переменную next_job. Увы, теперь мы имеем два потока, выполняющих одно и то же задание.

Далее ситуация только ухудшается. Первый поток удаляет последнее задание из очереди. делая переменную job_queue равной NULL. Когда второй поток попытается выполнить операцию job_queue->next, возникнет фатальная ошибка сегментации.

Это наглядный пример гонки за ресурсами. Если программе "повезет", система не распланирует потоки именно таким образом и ошибка не проявится. Возможно, только в сильно загруженной системе (или в новой многопроцессорной системе важного клиента!) произойдет "необъяснимый" сбой.

Чтобы исключить возможность гонки, необходимо сделать операции атомарными. Атомарная операция неделима и непрерывна; если она началась, то уже не может быть приостановлена или прервана, пока, наконец, не завершится. Выполнение других операций в это время становится невозможным. В нашем конкретном примере проверка переменной job_queue и удаление задания должны выполняться как одна атомарная операция.

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


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