Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 116 стр.

UptoLike

из занятых потоков. Такой подход обеспечивает, с одной стороны, малые
затраты на управление потоками, с другой – достаточно высокую загрузку
процессоров и хорошую масштабируемость приложения.
Обычно имеет смысл ограничивать общее число рабочих потоков ли-
бо числом доступных процессоров, либо кратным этому числом. Если по-
токи занимаются исключительно вычислительной работой, то их число не
должно превышать число процессоров. Если потоки, сверх того, проводят
некоторое время в состоянии ожидания (например, при выполнении опе-
раций ввода-вывода), то число потоков следует увеличить – решение сле-
дует принимать, исходя из доли времени, которое поток проводит в состо-
янии простоя, и из полного времени обработки запроса.
Достаточно типичная рекомендация: ограничивать число потоков
удвоенным числом процессоров. В случае вычислительных потоков на-
кладные потери будут достаточно малы; в случае потоков, занятых вво-
дом-выводом, утилизация процессоров будет близка к полной. Предпола-
гается, что потоки, не занятые вводом-выводом и при этом проводящие
много времени в состоянии ожидания, встречаются весьма редко.
Реализация пула потоков является, на самом деле, нетривиальной за-
дачей – необходимо поддерживать очередь запросов и учитывать состоя-
ния потоков из пула (поток простаивает; поток выполняется; поток вы-
полняется, но находится в состоянии ожидания). Также следует учитывать
возможность выгрузки части данных в файл подкачки (например, выгруз-
ка стека давно не используемого потока) – иногда быстрее подождать за-
вершения работающего потока, чем активировать простаивающий. Для
учета всех этих соображений необходимо реализовать поддержку пулов
потоков ядром операционной системы, так как на уровне приложения
некоторые нужные сведения просто недоступны.
В Windows такая поддержка реализована в виде порта завершения вво-
да-вывода. Этот объект ядра берет на себя функциональность, необходи-
мую для организации очереди запросов (используя для этого очередь APC)
и списков рабочих потоков, обеспечивая оптимальное управление пулом.
С точки зрения разработчика приложения необходимо:
создать порт завершения ввода-вывода;
создать пул потоков, ожидающий поступления запросов от это-
го порта;
обеспечить передачу запросов порту.
Порт завершения создается с помощью функции
HANDLE CreateIoCompletionPort(
HANDLE FileHandle, HANDLE ExistingCompletionPort,
ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads
);
Разработка параллельных приложений для ОС Windows
219
Глава 7.
Разработка параллельных приложений
для ОС Windows
7.1. Применение потоков и волокон
При разработке параллельных приложений недостаточно создать
несколько параллельных ветвей кода – необходимо обеспечить их согласо-
ванное выполнение и своевременный обмен данными. При этом возникает
необходимость использования как общих для разных потоков и процессов
данных, так и создания собственных локальных данных, недоступных дру-
гим параллельным ветвям кода, может потребоваться синхронизация и даже
учет особенностей аппаратуры, на которой данный код будет исполняться.
7.1.1. Пулы потоков, порт завершения ввода-вывода
Одна из типовых задач – разработка серверов, обслуживающих асин-
хронно поступающие запросы. Реализация однопоточного сервера для та-
кой задачи нецелесообразна, во-первых, потому что запросы могут прихо-
дить в то время, пока сервер занят выполнением предыдущего, а, во-вто-
рых, потому что такой сервер не сможет эффективно задействовать много-
процессорную систему. Можно, конечно, запускать несколько экземпля-
ров однопоточного сервера – но в этом случае потребуется разработка спе-
циального диспетчера, поддерживающего очередь запросов и их распреде-
ление по списку доступных экземпляров сервера. Альтернативным реше-
нием является разработка многопоточного сервера, создающего по специ-
альному рабочему потоку для обработки каждого запроса. Этот вариант
также имеет свои недостатки: создание и удаление потоков требует затрат
времени, которые будут иметь место в обработке каждого запроса; сверх
того, создание большого числа одновременно выполняющихся потоков
приведет к общему снижению производительности (и значительному уве-
личению времени обработки каждого конкретного запроса).
Эти соображения приводят к решению, получившему название пула
потоков (thread pool). Для реализации пула потоков необходимо создание
некоторого количества потоков, занятых обслуживанием запросов, и дис-
петчера с очередью запросов. При наличии необработанных запросов дис-
петчер находит свободный поток и передает запрос этому потоку; если
свободных потоков нет, то диспетчер ожидает освобождения какого-либо
218
CIL и системное программирование в Microsoft .NET
218                         CIL и системное программирование в Microsoft .NET   Разработка параллельных приложений для ОС Windows                   219


                                                                                из занятых потоков. Такой подход обеспечивает, с одной стороны, малые
Глава 7.                                                                        затраты на управление потоками, с другой – достаточно высокую загрузку
                                                                                процессоров и хорошую масштабируемость приложения.
Разработка параллельных приложений                                                   Обычно имеет смысл ограничивать общее число рабочих потоков ли-
                                                                                бо числом доступных процессоров, либо кратным этому числом. Если по-
для ОС Windows                                                                  токи занимаются исключительно вычислительной работой, то их число не
                                                                                должно превышать число процессоров. Если потоки, сверх того, проводят
                                                                                некоторое время в состоянии ожидания (например, при выполнении опе-
                                                                                раций ввода-вывода), то число потоков следует увеличить – решение сле-
7.1. Применение потоков и волокон                                               дует принимать, исходя из доли времени, которое поток проводит в состо-
                                                                                янии простоя, и из полного времени обработки запроса.
     При разработке параллельных приложений недостаточно создать                     Достаточно типичная рекомендация: ограничивать число потоков
несколько параллельных ветвей кода – необходимо обеспечить их согласо-          удвоенным числом процессоров. В случае вычислительных потоков на-
ванное выполнение и своевременный обмен данными. При этом возникает             кладные потери будут достаточно малы; в случае потоков, занятых вво-
необходимость использования как общих для разных потоков и процессов            дом-выводом, утилизация процессоров будет близка к полной. Предпола-
данных, так и создания собственных локальных данных, недоступных дру-           гается, что потоки, не занятые вводом-выводом и при этом проводящие
гим параллельным ветвям кода, может потребоваться синхронизация и даже          много времени в состоянии ожидания, встречаются весьма редко.
учет особенностей аппаратуры, на которой данный код будет исполняться.               Реализация пула потоков является, на самом деле, нетривиальной за-
                                                                                дачей – необходимо поддерживать очередь запросов и учитывать состоя-
7.1.1. Пулы потоков, порт завершения ввода-вывода                               ния потоков из пула (поток простаивает; поток выполняется; поток вы-
     Одна из типовых задач – разработка серверов, обслуживающих асин-           полняется, но находится в состоянии ожидания). Также следует учитывать
хронно поступающие запросы. Реализация однопоточного сервера для та-            возможность выгрузки части данных в файл подкачки (например, выгруз-
кой задачи нецелесообразна, во-первых, потому что запросы могут прихо-          ка стека давно не используемого потока) – иногда быстрее подождать за-
дить в то время, пока сервер занят выполнением предыдущего, а, во-вто-          вершения работающего потока, чем активировать простаивающий. Для
рых, потому что такой сервер не сможет эффективно задействовать много-          учета всех этих соображений необходимо реализовать поддержку пулов
процессорную систему. Можно, конечно, запускать несколько экземпля-             потоков ядром операционной системы, так как на уровне приложения
ров однопоточного сервера – но в этом случае потребуется разработка спе-        некоторые нужные сведения просто недоступны.
циального диспетчера, поддерживающего очередь запросов и их распреде-                В Windows такая поддержка реализована в виде порта завершения вво-
ление по списку доступных экземпляров сервера. Альтернативным реше-             да-вывода. Этот объект ядра берет на себя функциональность, необходи-
нием является разработка многопоточного сервера, создающего по специ-           мую для организации очереди запросов (используя для этого очередь APC)
альному рабочему потоку для обработки каждого запроса. Этот вариант             и списков рабочих потоков, обеспечивая оптимальное управление пулом.
также имеет свои недостатки: создание и удаление потоков требует затрат              С точки зрения разработчика приложения необходимо:
времени, которые будут иметь место в обработке каждого запроса; сверх                   • создать порт завершения ввода-вывода;
того, создание большого числа одновременно выполняющихся потоков                        • создать пул потоков, ожидающий поступления запросов от это-
приведет к общему снижению производительности (и значительному уве-                       го порта;
личению времени обработки каждого конкретного запроса).                                 • обеспечить передачу запросов порту.
     Эти соображения приводят к решению, получившему название пула                   Порт завершения создается с помощью функции
потоков (thread pool). Для реализации пула потоков необходимо создание
некоторого количества потоков, занятых обслуживанием запросов, и дис-                HANDLE CreateIoCompletionPort(
петчера с очередью запросов. При наличии необработанных запросов дис-                   HANDLE FileHandle, HANDLE ExistingCompletionPort,
петчер находит свободный поток и передает запрос этому потоку; если                     ULONG_PTR CompletionKey, DWORD NumberOfConcurrentThreads
свободных потоков нет, то диспетчер ожидает освобождения какого-либо                 );