Фиксированное количество нитей в многонитевой системе не есть уж очень хорошо. Зачастую не угадаешь пиковую нагрузку, а, если число реквестов превысит число нитей — то в некоторых случаях, когда обслуживание реквеста требует блокирующего надолго вызова — возникнет узкое место.
Потому используются динамические пулы нитей.
Как правильно имплементировать сие диво (если кому надо будет)?
Нить в данном случае есть ресурс. Если нить начала исполнять реквест — то количество доступных (ждущих реквеста на потребляющем конце очереди) нитей уменьшилось, возможно — до нуля.
Так вот, это "до нуля" тут же создает накопление реквестов в очереди, причем только потому, что еще не успели породить нить для их обслуживания, и реквест ждет либо такого порождения, либо пока закончится обслуживание еще какого-то реквеста. Второе — потеря скалабильности.
Потому разумно вот что: после того, как пуловая нить сняла реквест с очереди, она должна взглянуть на счетчик доступных нитей и, если он упал ниже какого-то значения, _тут же рожать еще одну нить_, выполняя реквест уже после этого. CreateThread — это достаточно быстро, по сравнению с, например, дисковой операцией или тем более SQL query.
В итоге накопление большой очереди надолго на пуле практически невозможно, по крайней мере если не ограничивать сверху число пуловых нитей. Как только наложен такой лимит — см. мои же посты о лимитах. При нагрузке большей, чем лимит — потеря скалабильности. При меньшей же нагрузке вообще все равно, есть лимит или нет.
Таковой лимит — как и остальные лимиты — имеет смысл только из соображений ограничения нагрузки на машину в целом. Кстати, основная нагрузка, создаваемая нитью — это кернел- и юзер- стеки.
Естественно, что имеет смысл сделать самоумирание нитей по таймауту в случае, если долго не было никакой нагрузки и так много нитей не нужно. Для этого ожидание на потребляющем конце очереди должно быть с таймаутом, и, если он наступил — то проверяется счетчик нитей, если он выше минимума — нить умирает.
Главное, думаю, понятно, остальное все напишут сами.
P.S. Ex/IoQueueWorkItem в ядре туп до блеска. Находясь под файловой системой, им пользоваться просто нельзя. Нельзя сделать queue work item из нити, которая сама есть work item — а путь записи на диск зачастую зовется из work item слива кэша.