Скачиваний:
90
Добавлен:
12.05.2015
Размер:
913.92 Кб
Скачать

15.2.3. Предотвращение тупиковых ситуаций

Поток может попасть в тупиковую ситуацию (deadlock), если попытается дважды запереть одно и то же взаимное исключение, но есть и менее очевидные способы. Например, тупиковая ситуация может возникнуть в случае, когда в программе используется более одного взаимного исключения. Допустим, что мы позволим одному потоку удерживать в запертом состоянии первое взаимное исключение и пытаться запереть второе, в то время как некоторый другой поток аналогичным образом удерживает в запертом состоянии второе взаимное исключение и пытается запереть первое. В этом положении ни один из потоков не сможет продолжить работу, поскольку каждый из них будет ждать освобождения ресурса, захваченного другим потоком, и в результате возникает тупиковая ситуация.

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

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

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

15.3. Условные переменные

Условные переменные – это еще один механизм синхронизации потоков. Они организуют для потоков своеобразное место встречи. Условные переменные используются только вместе со взаимными исключениями и позволяют потокам ожидать наступления некоторого события, избегая состояния гонок. Взаимное исключение используется для блокирования, а условная переменная – для ожидания наступления некоторого события и уведомления потоков о данном факте. Это два различных средства синхронизации, и оба они нужны.

Для работы с условными переменными определен специальный тип данных pthread_cond_t. Перед началом работы с условными переменными их необходимо инициализировать. При статическом размещении переменной ей следует присвоить значение константы PTHREAD_COND_INITIALIZER, но если условная переменная размещается динамически, нужно ее инициализировать с помощью вызова функции pthread_cond_init.

Для уничтожения условной переменной перед освобождением занимаемой ею памяти предназначена функция pthread_cond_destroy.

#include <pthread.h>

int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *attr);

int pthread_cond_destroy (pthread_cond_t *cond);

/* обе функции возвращают 0 в случае успеха, код ошибки – в случае неудачи */

Если в аргументе attr передается пустой указатель, переменная состояния будет инициализирована со значениями атрибутов по умолчанию. Атрибуты переменных состояния мы рассмотрим в разделе 12.4.

Соседние файлы в папке Chapter.4