883
Асинхронная.синхронизация
время как создание потока обходится слишком дорого, чтобы потом он не работал.
Для наглядности рассмотрим пример.
Представьте веб-сайт, к которому клиенты обращаются с запросами.
Посту-
пивший запрос начинает обрабатываться потоком из пула. Пусть клиент хочет
безопасным в отношении потоков способом изменить данные на сервере, поэтому
он получает блокировку на чтение-запись для записи. Представим, что блокирова-
ние длится долго. За это время успевает прийти еще один клиентский запрос, для
которого
пул создает новый поток, пытающийся получить блокировку на чтение-
запись для чтения. Запросы продолжают поступать, пул создает дополнительные
потоки, и все эти потоки блокируются. Все свое время сервер занимается созда-
нием потоков и не может остановиться! Такой сервер вообще не может нормально
масштабироваться.
Все становится только хуже, когда поток
записи снимает блокировку, и одновре-
менно запускаются все заблокированные потоки чтения. В результате относительно
небольшому количеству процессоров нужно как-то обработать все это множество
потоков. Windows попадает в ситуацию постоянного переключения контекста.
В результате весь объем работы выполняется не так быстро, как мог бы.
Многие из проблем, решаемые при помощи описанных в этой главе конструкций,
намного успешнее
решаются средствами класса
Task
, рассмотренного в главе 27.
К примеру, возьмем класс
Barrier
: для работы на каждом этапе можно было бы
создать группу заданий (объектов
Task
), а после их завершения ничто не мешает
нам продолжить работу с дополнительными объектами
Task
. Такой подход имеет
целый ряд преимуществ в сравнении с конструкциями, описанными в этой главе:
Задания требуют меньше памяти, чем потоки, кроме
того, они намного быстрее
создаются и уничтожаются.
Пул потоков автоматически распределяет задания среди доступных процессо-
ров.
По мере того как каждое задание завершает свой этап, выполнявший его поток
возвращается в пул, где может заняться другой работой, если таковая имеется.
Пул потоков видит все задания сразу и поэтому может лучше планировать их
выполнение, сокращая количество потоков в процессе, а
значит, и количество
переключений контекста.
Блокировки популярны, но при удержании в течение долгого времени они
создают серьезные проблемы с масштабированием. Было бы очень полезно иметь
асинхронные конструкции синхронизации, в которых ваш код сообщает о том, что
он хочет получить блокировку.
Если получить ее не удалось, он просто возвращает
управление для выполнения другой работы (вместо блокирования на неопреде-
ленное время). Затем, когда блокировка станет доступной, выполнение кода воз-
обновляется, и он может получить доступ к ресурсу, защищенному блокировкой. Эта
идея появилась у меня в процессе решения серьезных проблем масштабируемости