Performance & Scalability пост №4: thread pools
От: Maxim S. Shatskih Россия  
Дата: 17.08.07 00:16
Оценка: 14 (2)
Фиксированное количество нитей в многонитевой системе не есть уж очень хорошо. Зачастую не угадаешь пиковую нагрузку, а, если число реквестов превысит число нитей — то в некоторых случаях, когда обслуживание реквеста требует блокирующего надолго вызова — возникнет узкое место.

Потому используются динамические пулы нитей.

Как правильно имплементировать сие диво (если кому надо будет)?

Нить в данном случае есть ресурс. Если нить начала исполнять реквест — то количество доступных (ждущих реквеста на потребляющем конце очереди) нитей уменьшилось, возможно — до нуля.

Так вот, это "до нуля" тут же создает накопление реквестов в очереди, причем только потому, что еще не успели породить нить для их обслуживания, и реквест ждет либо такого порождения, либо пока закончится обслуживание еще какого-то реквеста. Второе — потеря скалабильности.

Потому разумно вот что: после того, как пуловая нить сняла реквест с очереди, она должна взглянуть на счетчик доступных нитей и, если он упал ниже какого-то значения, _тут же рожать еще одну нить_, выполняя реквест уже после этого. CreateThread — это достаточно быстро, по сравнению с, например, дисковой операцией или тем более SQL query.

В итоге накопление большой очереди надолго на пуле практически невозможно, по крайней мере если не ограничивать сверху число пуловых нитей. Как только наложен такой лимит — см. мои же посты о лимитах. При нагрузке большей, чем лимит — потеря скалабильности. При меньшей же нагрузке вообще все равно, есть лимит или нет.

Таковой лимит — как и остальные лимиты — имеет смысл только из соображений ограничения нагрузки на машину в целом. Кстати, основная нагрузка, создаваемая нитью — это кернел- и юзер- стеки.

Естественно, что имеет смысл сделать самоумирание нитей по таймауту в случае, если долго не было никакой нагрузки и так много нитей не нужно. Для этого ожидание на потребляющем конце очереди должно быть с таймаутом, и, если он наступил — то проверяется счетчик нитей, если он выше минимума — нить умирает.

Главное, думаю, понятно, остальное все напишут сами.

P.S. Ex/IoQueueWorkItem в ядре туп до блеска. Находясь под файловой системой, им пользоваться просто нельзя. Нельзя сделать queue work item из нити, которая сама есть work item — а путь записи на диск зачастую зовется из work item слива кэша.

Дедлоки из этой имплементации летят только так.
Занимайтесь LoveCraftом, а не WarCraftом!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.