|
В современном операционном окружении программист
не может быть уверен и не должен полагаться на
то, что коды его программы будут выполняться в
тон же последовательности, в какой они написаны.
Выполнение одной из функций программы может быть
остановлено системой и возобновлено позднее,
причем это может произойти даже при выполнении
тела какого-либо цикла. При проектировании
многопотоковых приложений следует иметь в виду,
что ресурсы, разделяемые потоками (блоки памяти
или файлы), можно неосознанно повредить. Чтобы
показать, как это происходит, рассмотрим пример,
который приведен в книге Jesse Liberty «Beginning
Object-Oriented Analysis and Design with C++»
(Дж. Либерти «Начало объектно-ориентированного
анализа и проектирования с помощью C++»),
доступной в MSDN.
Представьте себе пассажирский авиалайнер в
полете, а в нем такой разделяемый всеми ресурс,
как туалетная комната. Создатели самолета
предполагали, что только одна персона может
занимать эту комнату. Первый, кто ее занял,
закрывает (lock) доступ к пей для всех
остальных. Следующий пассажир, желающий
воспользоваться этим ресурсом, может либо
терпеливо ожидать освобождения, либо по
истечении какого-то времени (time out) вернуться
на свое сиденье и продолжать заниматься тем, чем
он был занят до этого события. Решение о том,
что выбрать и как долго ждать, принимает
пассажир. Блокирование ресурса порождает
неэффективное проведение времени второго
пассажира как ожидающего очереди, так и
избравшего другую тактику.
Возвращаясь к многопотоковым процессам, отметим,
что если не блокировать ресурс, то
становится возможным повреждение данных.
Представьте, что один поток процесса проходит по
записям базы данных, повышая зарплату каждому
сотруднику на 10%, а другой поток в это же время
изменяет почтовые индексы в связи с введением
нового стандарта. Согласитесь с тем, что разумно
совместить эти две работы в одном процессе с
целью повышения производительности. Что может
произойти, если не блокировать доступ к записи
при ее модификации? Первый поток прочел запись
(все ее поля), и занят вычислением повышения
(предположим, с $80 000 до $85 000). В это время
второй поток читает эту же запись с целью
изменения почтового индекса. В этой ситуации
может произойти следующее: первый поток
сохраняет измененную запись с новым значением
зарплаты, а второй, возвращая запись с
измененным индексом, реставрирует значение
зарплаты и данный сотрудник останется без
повышения. Это происходит по причине того, что
оба потока не могут обратиться к части записи и
поэтому работают со всей записью, хотя
модифицируют только отдельные ее поля. |