Реферат: Эффективная многопоточность
);
QueueUserWorkItem помещает пакет запроса в виде адреса функции и произвольного параметра в очередь запросов порта завершения и сразу же возвращает управление. Вот как выглядит функция, которая будет вызвана одним из потоков в пуле:
DWORD WINAPI ThreadProc( LPVOID lpParameter // произвольныйпараметр ); |
Ее прототип ничем не отличается от стартовой процедуры потока, так что здесь вам все должно быть ясно. Гораздо интереснее знать, что скрывается внутри функции QueueUserWorkItem. Давайте разбираться.
При первом помещении запроса количество потоков в пуле равно нулю, так что QueueUserWorkItem приходится создавать поток и порт завершения. Затем в порт помещается пакет запроса, а поток вызывает функцию GetQueuedCompletionStatus. После обработки запроса поток не разрушается, а остается еще некоторое время в пуле, так что следующий запрос обработается намного быстрее. Если вы отправляете запросы слишком часто, и количество необработанных пакетов увеличивается, QueueUserWorkItem создаст для вызова функции новый поток. Максимальное количество потоков в пуле равно количеству процессоров, что не очень хорошо, но есть способ заставить функцию всегда создавать новый поток.
ПРИМЕЧАНИЕ Те из вас, кто читал статью Дж. Рихтера «New Windows 2000 Pooling Functions Greatly Simplify Thread Management» из апрельского MSJ за 1999 год, могут поспорить со мной насчет размера пула. В статье указывается, что количество потоков в нем равно удвоенному количеству процессоров в системе, однако это не так. Вы можете собственноручно в этом убедиться, поставив breakpoint на функцию _RtlpInitializeWorkerThreadPool (адрес 0x77FA95CD на Windows 2000 Professional SP3) и вызвав функцию QueueUserWorkItem. |
Рассмотрим флаги функции QueueUserWorkItem.
Константа | Значение | Описание |
WT_EXECUTEDEFAULT | 0 | Запрос помещается в простой рабочий поток |
WT_EXECUTEINIOTHREAD | 1 | Запрос помещается в поток ввода/вывода |
WT_EXECUTEINPERSISTENTTHREAD | 0x80 | Запрос помещается в поток, который не завершается после обработки запроса, поэтому он может сохранять свое состояние, например в TLS. |
WT_EXECUTELONGFUNCTION | 0x10 | Запрос с данным флагом всегда помещается в новый поток |
Таблица 3. Флаги функции QueueUserWorkItem.
Если вы не выполняете асинхронных запросов ввода/вывода в функции ThreadProc, не используете TLS (Thread Local Storage) или функций, которые его используют, а продолжительность выполнения операции невелика – указывайте флаг WT_EXECUTEDEFAULT.
Предположим, вы начали асинхронную операцию ввода/вывода в своей функции ThreadProc. Для того чтобы она завершилась, поток в котором она началась, не должен быть разрушен. Однако флаг WT_EXECUTEDEFAULT этого не гарантирует. С этим флагом поток может быть удален, даже если у него имеются незавершенные асинхронные операции. Для того чтобы поток завершался только после окончания всех начатых асинхронных операций, нужно указать флаг WT_EXECUTEINIOTHREAD.
При указании флага WT_EXECUTEINPERSISTENTTHREAD ?