Реферат: Эффективная многопоточность

поток должен быть в состоянии тревожного ожидания, чтобы обработать APC-запрос;

второй APC-запрос начнет обрабатываться только после окончания обработки предыдущего запроса, то есть запросы обрабатываются последовательно.

Все эти проблемы решает объект "очередь таймеров", о котором речь пойдет позже.

Порт завершения ввода/вывода

Это, безусловно, один из самых мощных и сложных объектов исполнительной системы. Он специально предназначен для оптимизации обработки клиентских запросов в серверных приложениях. Он не только организует очередь запросов, но и эффективно управляет их обработкой.

Основная идея порта завершения ввода/вывода (в дальнейшем просто порта) состоит в том, чтобы эффективно расходовать процессорное время при обработке клиентских запросов. Обработка должна вестись параллельно несколькими потоками, но строго до определенного момента, когда число потоков будет равняться максимальному значению. После этого новые потоки перестают создаваться, а запросы ставятся в очередь к существующим потокам. Давайте разберемся в этом поподробнее.

Как работает порт

При создании порта указывается максимальное количество активных потоков, способных обрабатывать клиентские запросы параллельно. Так как количество реально работающих параллельно потоков на компьютере равно количеству процессоров, то указание большего максимального количества активных потоков не выгодно. Почему? Дело в том, что для исполнения нескольких потоков на одном процессоре, системе приходится постоянно переключать процессор между потоками, эмулируя, таким образом, параллельность, однако это переключение, называемое переключением контекстов – довольно дорогая операция. Избежать ее можно только одним способом – не создавать параллельно работающие потоки в количестве большем, чем число процессоров. Таким образом, при создании порта, казалось бы, нужно указывать в качестве максимального количества активных потоков число процессоров в системе, но здесь есть одна тонкость. Допустим, у нас однопроцессорный компьютер и, соответственно, клиентские запросы мы обрабатываем в одном потоке. Что будет, если клиентский запрос придет в момент выполнения синхронной операции с диском или в момент ожидания какого-либо объекта этим потоком? Он будет ждать, пока поток не закончит свою работу, но ведь процессор в это время бездействует, потому что поток заблокирован на синхронной операции или на каком-либо объекте. Когда процессор бездействует, а клиентский запрос не обрабатывается – это плохо. Мы приходим к выводу о том, что всегда должен существовать резервный поток, который подхватывал бы запросы в момент, когда «основной» поток выполняет блокирующие операции, и процессор бездействует.

Работа с файлами (в самом широком смысле слова) очень тесно связана с многопоточностью и обработкой запросов на сервере. Сокет или pipe – это тоже файлы. Чтобы обрабатывать запросы через эти каналы параллельно, нужен порт. Давайте рассмотрим функцию создания порта и связи его с файлом (зачем-то разработчики из Microsoft объединили две эти функции в одну; в исполнительной системе эти две функции выполняют сервисы NtCreateIoCompletion и NtSetInformationFile, соответственно).

HANDLE CreateIoCompletionPort (

HANDLE FileHandle, // хендлфайла

HANDLE ExistingCompletionPort, // хендл порта завершения ввода/вывода

ULONG_PTR CompletionKey, // ключ завершения

DWORD NumberOfConcurrentThreads // максимальное число параллельных потоков

);

Для простого создания порта нужно в качестве первого параметра передать INVALID_HANDLE_VALUE, а в качестве второго и третьего – 0. Для связывания файла с портом нужно указать первые три параметра и проигнорировать четвертый.

После того, как файл (под файлом здесь подразумевается объект подсистемы Win32, который реализуется с помощью объекта "файл исполнительной системы", к таковым относятся файлы, сокеты, почтовые ящики, именованные каналы и проч.) связан с портом, окончания всех асинхронных запросов ввода/вывода попадают в очередь порта и могут быть обработаны пулом потоков. Следующие функции могут быть использованы с портом завершения для обработки асинхронных операций ввода/вывода:

ConnectNamedPipe – ожидает подключения клиента к именованному каналу.

DeviceIoControl – низкоуровневый ввод/вывод.

LockFileEx – блокировка региона файла.

ReadDirectoryChangesW – ожидание изменений в директории.

ReadFile – чтение файла.

TransactNamedPipe – Комбинированное чтение и запись по именованному каналу, осуществляемые за одну сетевую операцию.

WaitCommEvent – ожидание события последовательного интерфейса (СОМ-порт).

WriteFile – запись в файл.

Если вы не хотите, чтобы окончание асинхронного ввода/вывода обрабатывалось портом (например, когда вам не важен результат операции), нужно использовать следующий трюк [1]. Нужно установить поле hEvent структуры OVERLAPPED равным описателю события с установленным первым битом. Делается это примерно так:

OVERLAPPED ov = {0};

ov.hEvent = CreateEvent(...);

ov.hEvent = (HANDLE)((DWORD_PTR)(ov.hEvent) | 1);

И не забывайте сбрасывать младший бит при закрытии хендла события.

Добавлять поток к пулу (подключать его к обработке запросов) можно с помощью следующей функции:

К-во Просмотров: 413
Бесплатно скачать Реферат: Эффективная многопоточность