Тупиковая
ситуация (Deadlock)
Выше мы рассмотрели,
как потоки одного процесса могут вступить в конфликт и испортить работу друг
друга. Одной из задач программиста является обеспечить невозможность такого
сценария. Другими возможными неприятностями могут быть: рассинхронизация (race
conditions) и тупиковая ситуация (deadlock).
Первая может
произойти, когда успех одной операции зависит от успеха другой, но обе они не
синхронизированы друг с другом. Предположим, что один поток процесса подготавливает
принтер, а другой ставит задание на печать (print job) в очередь. Если
потоки не синхронизированы и первый из них не успеет выполнить свою работу до
того, как начнется печать, то мы получим сбой.
Примечание
Но в каком-то количестве
случаев все пройдет гладко. Такой тип ошибок очень неприятен, так как в процессе
отладки ее нельзя уверенно и многократно воспроизводить. Рассинхронизация
порождает ненадежность —тип ошибок, который большинство программистов всего
мира ненавидит. В MSDN, но, к сожалению, не в литературе, вы часто можете
встретить упоминания о коварстве irreprodudble bugs (невоспроизводимые ошибки).
Суверенностью можно сказать, что книга под названием «Технологии борьбы с
ошибками» была бы бестселлером.
Тупиковая ситуация
создается, когда один поток ждет завершения второго, а второй ждет завершения
первого. Представьте, что один поток реализует такую функцию:
- блокирует запись, идентифицирующую
клиента;
- блокирует запись, описывающую
его счет;
- изменяет обе записи;
- освобождает запись, описывающую
счет;
- освобождает запись, идентифицирующую
клиента.
Обратите внимание
на то, что освобождение блокировок происходит в обратном порядке. Именно так
следует поступать при работе с записями базы данных и всеми объектами ядра Windows.
Предположим далее, что второй поток реализует функцию начисления месячного процента
и он делает те же действия, что и первый, но порядок блокирования и освобождения
записей обратный. Оба потока по отдельности функционируют вполне надежно. В
процессе работы возможен следующий сценарий: первый поток блокирует запись,
идентифицирующую клиента, затем второй блокирует запись, описывающую его счет.
После этого оба ждут освобождения записей, блокированных друг другом. Если ожидание
реализовано разработчиком в виде бесконечного цикла, то мы его получили. Это
тупиковая ситуация, или deadlock.