Обычно пул потоков не включается в цикл сообщений Windows, и блокирование на неопределенный срок, когда нет работы, не только допустимо для рабочего потока, но даже желательно .
Самый элегантный способ реализации пула потоков, который может принимать сообщения через какую-то очередь, который автоматически поддерживает занятость всех ядер ЦП и который в качестве бонуса очень эффективен, использует порт завершения.
CreateIoCompletionPort
с дескриптором null создаст порт завершения и вернет дескриптор. Передача нуля, когда NumberOfConcurrentThreads
указывает операционной системе поддерживать столько потоков, сколько доступно ядер.
Создайте любое количество рабочих потоков (на несколько больше, чем у вас ядер) и CreateIoCompletionPort
с дескриптором, возвращаемым при первом вызове. Это свяжет рабочих с этим портом завершения. Теперь позвоните GetQueuedCompletionStatus
с INFINITE
тайм-аутом на каждого работника, который заблокирует их на неопределенный срок.
Создайте struct
, у которого в качестве первого члена есть ПЕРЕКРЫТИЕ, плюс любые данные, которые вы хотите передать как задачу (некоторые указатели на данные или что-то еще).
Для каждой задачи настройте одну из своих структур сообщений и PostQueuedCompletionStatus
для дескриптора порта завершения. При выходе из приложения, отправьте ноль. Вы можете использовать поле dwNumberOfBytesTransferred
(и ключ завершения) для передачи дополнительной информации.
Теперь Windows будет пробуждать одну ветку для каждого отправленного вами сообщения, в порядке очередности до количества доступных ядер. Если один из рабочих блоков в IO блокируется, Windows пробуждает другого для другой задачи (если процессор занят, пока есть работа).
После завершения задания вернитесь к GetQueuedCompletionStatus
.
Чтобы изящно завершить работу всех работников, нужно передать «ноль переданных байтов» и заставить работника повторно опубликовать событие и выйти, если он с этим столкнется.