Спящий рабочий общаться не хочет
Делаем большой интересный серверный проект. В прошлом месяце начальство внезапно постановило: нужно, чтобы сервер умел вставать на паузу! Ну, и ТЗ, как он это должен делать. В частности, перед уходом на паузу сервер должен успеть обработать и выдать все принятые пакеты. Архитектура многопоточная: сетевой поток, занимающийся как отправкой, так и приёмом пакетов, UI-поток, принимающий и обрабатывающий пользовательские команды, SQLworkers, UtilityWorkers и куча рабочих потоков.
Все потоки умеют обмениваться данными только через очередь сообщений. Поэтому, как только добавлено новое сообщение в очередь, поток снимается с паузы, шлёт запрос потоку UI — надо ли ему становиться на паузу, потом обрабатывает сообщение и нормально работает, пока не придёт пакет с подтверждением паузы. Но ТЗ есть, так что мы его тупо реализуем, не особо задумываясь.
Реализовали, отправили тестировщикам. На следующий день пришёл ответ.
Я был сильно озадачен результатом применения паузы под серьёзной нагрузкой. Сперва поток UI рассылает всем приказ встать на паузу, затем сам становится на паузу. NetWorker моментально становится на паузу: он очень быстро всё принимает и отправляет. Воркеры так же быстро становятся на паузу, а вот один из SQLworkers при обработке запросов
INSERT/UPDATE [имя очень тяжёлой таблицы]
может очень долго ждать ответа от БД. И как только он его получит, он сделает запись в лог об этом, разбудив поток UtilityWorker, потом кидает результат в общий пул сообщений, разбудив пул воркеров, потом этот пакет будит NetWorker. В то же время поток UIworker не пробуждается от запроса в очередь сообщений.В итоге мы получаем полностью рабочий сервис со спящим потоком UIworker, разбудить который может только пользователь… Который сделать этого не может, ибо спящий UIworker не обрабатывает поступающие команды.
Теперь поток UI никогда не может встать на паузу.