Здавалка
Главная | Обратная связь

Взаимные блокировки потоков



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

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

Для большей ясности можно рассмотреть классический пример возникновения взаимной блокировки. Допустим, имеется два потока (или процесса), один из которых занимает некоторый ресурс A, а другой – ресурс B и оба каким-то образом с ними работают (рис. 18). По прошествии некоторого времени первому потоку для завершения своей работы с ресурсом A требуется получить доступ к занятому вторым потоком ресурсу B. Аналогичная ситуация у второго потока – для завершения его работы с ресурсом B ему необходимо получить доступ к занятому первым потоком ресурсу A. Оба потока в данном случае будут находиться в режиме ожидания доступа к нужному ресурсу. Если время ожидания никак не ограничено, то оно будет бесконечным и вызовет «зависание» программы.

Рисунок 18 – Взаимная блокировка потоков

Программная реализация описанной выше ситуации могла бы выглядеть следующим образом:

CRITICAL_SECTION A;

CRITICAL_SECTION B;

 

// Функция потока A

DWORD WINAPI ThreadA(PVOID param)

{

// Захват ресурса A

EnterCriticalSection(&A);

 

...

 

// Ожидание ресурса B

EnterCriticalSection(&B);

 

...

 

LeaveCriticalSection(&B);

 

LeaveCriticalSection(&A);

 

return 0;

}

 

// Функция потока B

DWORD WINAPI ThreadB(PVOID param)

{

// Захват ресурса B

EnterCriticalSection(&B);

 

...

 

// Ожидание ресурса A

EnterCriticalSection(&A);

 

...

 

LeaveCriticalSection(&A);

 

LeaveCriticalSection(&B);

 

return 0;

}

В данном примере объявлено две критические секции – A и B, каждая из которых защищает соответствующий ресурс. Поток ThreadA входит в критическую секцию A, а через некоторое время ожидает доступ к ресурсу, который защищает критическая секция B. Аналогично поток ThreadB начинает свою работу со входа в критическую секцию B и через некоторое время ожидает доступ к ресурсу, который защищает критическая секция A. Если какой-то из потоков успеет раньше другого захватить оба ресурса, взаимной блокировки не произойдет, в противном случае программа зависнет: оба потока будут ожидать освобождения нужного ресурса, причем ожидание будет длиться бесконечно, поскольку критические секции не позволяют указать предельное время ожидания.

Подобная ситуация может быть решена введением одной критической секции, которая будет защищать оба ресурса: и ресурс A и ресурс B. Однако данное решение не является единственно правильным, поскольку, в конечном счете, все зависит от решаемой задачи.

CRITICAL_SECTION A_B;

 

// Функция потока A

DWORD WINAPI ThreadA(PVOID param)

{

// Захват ресурса A и B

EnterCriticalSection(&A_B);

 

...

 

// Освобождение ресурсов

LeaveCriticalSection(&A_B);

 

return 0;

}

 

// Функция потока B

DWORD WINAPI ThreadB(PVOID param)

{

// Захват ресурса A и B

EnterCriticalSection(&A_B);

 

...

 

// Освобождение ресурсов

LeaveCriticalSection(&A_B);

 

return 0;

}

Таким образом, об устранении взаимных блокировок следует заботиться еще на этапе проектирования системы. Это единственный более-менее надежный способ бороться с ними.

 

Пулы потоков

Начиная с Windows 2000 в операционной системе появились функции по работе с пулами потоков. Эти функции упрощают создание, уничтожение и общий контроль над потоками; дают возможность вызывать другие функции асинхронно, через определенные промежутки времени, при освобождении отдельных объектов ядра или при завершении запросов на асинхронный ввод-вывод.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.