Книга: Программирование для 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
и удаление задания должны выполняться как одна атомарная операция.
- 4.4.1. Состояние гонки
- Листинг 10.1. (simpleid.c) Отображение идентификаторов пользователя и группы
- 2.1.3. Функция getopt_long()
- Группировка по встроенным функциям и UDF
- Модули констукторов заданий
- 19.1.1. Функция jQuery()
- Функция strcmp( )
- NFQUEUE target
- QUEUE target
- Листинг 15.11. Код для загрузки файла с Web-сервера
- Управление функциями узла
- Функция программного обеспечения