Насколько масштабируема система.Многопоточность.Таймер?
-
09-06-2019 - |
Вопрос
Я пишу приложение, в котором нужно будет использовать Timer
s, но потенциально их очень много.Насколько масштабируема система System.Threading.Timer
класс?В документации просто говорится, что она "легкая", но ничего больше не объясняется.Засасываются ли эти таймеры в один поток (или очень маленький threadpool), который обрабатывает все обратные вызовы от имени Timer
, или же каждый Timer
есть свой собственный поток?
Я думаю, что другой способ перефразировать вопрос таков:Как это System.Threading.Timer
реализовано?
Решение
Я говорю это в ответ на множество вопросов:Не забывайте, что (управляемый) исходный код фреймворка доступен.Вы можете использовать этот инструмент, чтобы получить все это: http://www.codeplex.com/NetMassDownloader
К сожалению, в данном конкретном случае большая часть реализации находится в машинном коде, так что вы не сможете на это взглянуть...
Однако они определенно используют потоки пула, а не поток на таймер.
Стандартный способ реализации большой коллекции таймеров (именно так ядро делает это внутренне, и я бы подозревал, что косвенно так заканчивается ваша большая коллекция таймеров) - поддерживать список, отсортированный по времени до истечения срока действия, поэтому системе приходится беспокоиться только о проверке следующего таймера, срок действия которого истекает, а не всего списка.
Грубо говоря, это дает O (log n) для запуска таймера и O (1) для обработки запущенных таймеров.
Редактировать:Только что заглянул в книгу Джеффа Рихтера.Он говорит (о потоковой обработке.Timer), что он использует один поток для всех объектов Timer, этот поток знает, когда наступит следующий таймер (т.е.как указано выше) выполняется и вызывает ThreadPool.QueueUserWorkItem для обратных вызовов по мере необходимости.Это приводит к тому, что если вы не завершите обслуживание одного обратного вызова по таймеру до наступления следующего, ваш обратный вызов будет повторно выполнен в другом потоке пула.Итак, подводя итог, я сомневаюсь, что вы увидите большую проблему с большим количеством таймеров, но вы можете столкнуться с истощением пула потоков, если большое количество из них запускается с одним и тем же таймером и / или их обратные вызовы выполняются медленно.
Другие советы
Я думаю, вам, возможно, захочется переосмыслить свой дизайн (то есть, если вы сами контролируете дизайн).Если вы используете так много таймеров, что это действительно вызывает у вас беспокойство, то здесь явно есть некоторый потенциал для консолидации.
Вот хорошая статья из журнала MSDN, опубликованная несколько лет назад, в которой сравниваются три доступных класса таймеров и дается некоторое представление об их реализации:
Объедините их.Создайте службу таймера и запросите ее для таймеров.Для этого нужно будет поддерживать активным только 1 таймер (для следующего вызова)...
Чтобы это было улучшением по сравнению с простым созданием большого количества объектов Threading.Timer, вы должны предположить, что это не совсем то, что Threading.Timer уже выполняет внутренне.Мне было бы интересно узнать, как вы пришли к такому выводу (я не разобрал исходные части фреймворка, так что вы вполне можете быть правы).
^^ как говорит ДэнниСмерф :Объедините их.Создайте службу таймера и запросите ее для таймеров.Потребуется сохранить только 1 активный таймер (для следующего своевременного вызова) и историю всех запросов таймера и пересчитать это в AddTimer() / RemoveTimer().