Достаточно ли эффективен System.Threading.Timer для тысяч одновременных таймеров?

Я работаю над механизмом тайм-аута запроса. Мой первоначальный подход состоял в том, чтобы создать один System.Threading.Timer для каждого запроса. Количество одновременных запросов может достигать тысяч.

Мне интересно, должен ли я вместо этого создать TimeoutScheduler, который будет внутренне использовать только один таймер вместо одного для каждого запроса.

Может ли кто-нибудь, кто знает внутренности System.Threading.Timer, дать мне некоторое представление о том, будет ли TimeoutScheduler хорошей идеей или он только попытается оптимизировать что-то уже достаточно эффективное.

Примечание. Для моего сценария точность таймера не важна.

(Я провел тест производительности с помощью System.Threading.Timer с множеством одновременных таймеров. Казалось, он хорошо масштабируется, но я не уверен, что это окажет нежелательное давление на реальную систему)


person Jeff Cyr    schedule 03.03.2010    source источник


Ответы (5)


Я направлю вас к этому сообщению от Рэймонда Чена:

Какое максимальное количество таймеров может создать программа?

Технически нет никаких проблем с созданием их тысяч. Просто имейте в виду, что они используют глобальные системные ресурсы, поэтому вы в конце концов достигнете верхнего предела и/или начнете голодать другие программы (включая саму Windows), если сойдете с ума.

Также убедитесь, что вы утилизируете таймеры, когда закончите с ними, иначе вы столкнетесь с огромной утечкой ресурсов.


Я должен сказать, что это похоже на то, что вы могли бы реализовать с помощью одного таймера; просто поддерживайте словарь активных запросов и тайм-аутов, и на каждом тике просматривайте весь словарь и проверяйте каждую запись. Вы можете улучшить производительность, сделав его отсортированным списком; таким образом, если в течение следующих 5 минут ничего не истекло, метод тика завершится после просмотра первой записи.

В дополнение к вышесказанному вы также можете динамически настроить интервал тактов, чтобы он срабатывал всякий раз, когда следующий запрос запланирован на истечение времени ожидания; затем на каждом тике перепланируйте следующий тик на следующий тайм-аут. Это практически не потребует системных ресурсов (только один таймер), а также будет очень быстрым (как и в случае с вашей идеей с одним таймером на запрос, вы будете запускать дорогостоящий код только тогда, когда запросы действительно истекают).

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

person Aaronaught    schedule 03.03.2010
comment
В статье Рэймонда Чена описывается таймер другого типа. - person SLaks; 04.03.2010
comment
@SLaks: в статье Рэймонда Чена не описывается какой-либо конкретный тип таймера. В конце он мимоходом упоминает таймер Windows Forms, и я уверяю вас, что прекрасно осознаю разницу, но все таймеры .NET обертывают таймеры Windows и подчиняются одному и тому же правилу. предостережения. - person Aaronaught; 04.03.2010
comment
Собственно, в статье Рэймонда Чена описаны таймеры, созданные SetTimer. System.Threading.Timer на самом деле является таймером очереди таймера (msdn. microsoft.com/en-us/library/ms686796(v=VS.85).aspx), который по существу представляет собой единый таймер и эффективный механизм определения момента запуска следующего события. - person Jim Mischel; 03.09.2010

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

person Wiseheathen    schedule 03.03.2010

System.Threading.Timer выполняет свой TimerCallback в ThreadPool, создание 1000 может привести к голоданию пула потоков.

MSDN System.Threating.Timer

Используйте делегат TimerCallback, чтобы указать метод, который должен выполнять таймер. Делегат таймера указывается при создании таймера и не может быть изменен. Метод не выполняется в потоке, создавшем таймер; он выполняется в потоке ThreadPool, предоставленном системой.

System.Timers.Timer ведет себя точно так же. TimeoutScheduler — это то, что нужно.

person ParmesanCodice    schedule 03.03.2010

Я знаю, что это старо, но эти ответы противоречат тому, что я всегда делал, поэтому я проверил справочный источник, и все они неверны.

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

person Gusman    schedule 23.09.2019
comment
Привет, Гусман, многое изменилось в .NET Core со времени моего первоначального вопроса. Для справки: исходная реализация в .NET Framework была оптимизирована для случаев, когда таймер редко достигает нужного времени (например, тайм-ауты). Добавление и отмена таймера — операция O(1). Однако, когда таймер срабатывает, он выполняет линейный поиск по всем таймерам, чтобы найти следующий таймер для срабатывания. Таким образом, все может пойти наперекосяк, если у вас есть большое количество таймеров тайм-аута И некоторые таймеры, которые срабатывают с небольшим интервалом. Я не измерял с .NET Core, но в 2.1 и 3.0 была проведена значительная оптимизация. - person Jeff Cyr; 24.09.2019
comment
В итоге я реализовал хешированное колесо синхронизации, cs. columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf - person Jeff Cyr; 24.09.2019

Похоже на работу для quartz.net.

person kenny    schedule 03.03.2010