Чтение онлайн

на главную - закладки

Жанры

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

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

Шрифт:
Листинг 4.9. (cxx-exit.cpp) Безопасное завершение потока в C++

#include <pthread.h>

class ThreadExitException {

public:

 /* Конструктор, принимающий аргумент RETURN_VALUE, в котором

содержится возвращаемое потоком значение. */

 ThreadExitException(void* return_value) :

thread_return_value_(return_value) {

 }

 /*
Реальное завершение потока. В программу возвращается

значение, переданное конструктору. */

 void* DoThreadExit {

pthread_exit(thread_return_value_);

 }

private:

 /* Значение, возвращаемое в программу при завершении потока. */

 void* thread_return_value_;

};

void do_some_work {

 while (1) {

/* Здесь выполняются основные действия... */

if (should_exit_thread_immediately)

throw ThreadExitException(/* поток возвращает */NULL);

 }

}

void* thread_function(void*) {

 try {

do_some_work;

 } catch (ThreadExitException ex) {

/* Возникла необходимость завершить поток. */

ex.DoThreadExit;

 }

 return NULL;

}

4.4. Синхронизация потоков и критические секции

Программирование потоков — нетривиальная задача, ведь большинство потоков выполняется одновременно. К примеру, невозможно определить, когда система предоставит доступ к процессору одному потоку, а когда — другому. Длительность этого доступа может быть как достаточно большой, так и очень короткой, в зависимости от того, как часто система переключает задания. Если в системе есть несколько процессоров, потоки могут выполняться одновременно в буквальном смысле.

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

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

4.4.1. Состояние

гонки

Предположим, что в программу поступает группа запросов, которые обрабатываются несколькими одновременными потоками. Очередь запросов представлена связанным списком объектов типа

struct job
.

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

job_queue
не равен
NULL
, поток удаляет из списка самый верхний элемент и перемещает указатель на следующий элемент. Потоковая функции, работающая с очередью заданий, представлена в листинге 4.10.

Листинг 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
, возникнет фатальная ошибка сегментации.

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

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

Последний Паладин. Том 4

Саваровский Роман
4. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 4

Газлайтер. Том 17

Володин Григорий Григорьевич
17. История Телепата
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Газлайтер. Том 17

Тот, кто знает. Книга вторая. Перекресток

Маринина Александра
5.00
рейтинг книги
Тот, кто знает. Книга вторая. Перекресток

Опыт дурака-3. Как жить и добра наживать. Самостоятельное изготовление семейного счастья в домашних условиях

Норбеков Мирзакарим Санакулович
Дом и Семья:
здоровье и красота
6.80
рейтинг книги
Опыт дурака-3. Как жить и добра наживать. Самостоятельное изготовление семейного счастья в домашних условиях

12

Санивский Сергей Анатолиевич
5.00
рейтинг книги
12

Кто изобрел современную физику? От маятника Галилея до квантовой гравитации

Горелик Геннадий
Научно-образовательная:
история
физика
6.25
рейтинг книги
Кто изобрел современную физику? От маятника Галилея до квантовой гравитации

An ordinary sex life

Астердис
Любовные романы:
современные любовные романы
love action
5.00
рейтинг книги
An ordinary sex life

Первый среди равных. Книга III

Бор Жорж
3. Первый среди Равных
Фантастика:
попаданцы
аниме
фэнтези
6.00
рейтинг книги
Первый среди равных. Книга III

Первый среди равных. Книга XIV

Бор Жорж
14. Первый среди Равных
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Первый среди равных. Книга XIV

Ловец мудрости

A.E. Giggle
Фантастика:
фэнтези
5.00
рейтинг книги
Ловец мудрости

"Универсальное государство" в России. От древних времен до Крымской войны

Галимов Брячеслав
Научно-образовательная:
история
5.00
рейтинг книги
Универсальное государство в России. От древних времен до Крымской войны

АН (цикл 11 книг)

Тарс Элиан
Аномальный наследник
Фантастика:
фэнтези
героическая фантастика
попаданцы
аниме
5.60
рейтинг книги
АН (цикл 11 книг)

Государство и революции

Шамбаров Валерий Евгеньевич
Научно-образовательная:
история
7.86
рейтинг книги
Государство и революции

Записки духовного авантюриста

Блект Рами
Научно-образовательная:
психология
7.00
рейтинг книги
Записки духовного авантюриста