Я говорю это в ответ на множество вопросов: не забывайте, что (управляемый) исходный код платформы доступен. Вы можете использовать этот инструмент, чтобы получить все это: http://www.codeplex.com/NetMassDownloader
К сожалению, в этом конкретном случае, большая часть реализации находится в нативном коде, поэтому вы не можете смотреть на это ...
Хотя они определенно используют потоки пула, а не поток по таймеру.
Стандартный способ реализовать большую коллекцию таймеров (как ядро делает это внутренне, и я подозреваю, косвенно, как заканчивается ваша большая коллекция таймеров) - поддерживать список, отсортированный по времени до истечения срока действия. - поэтому система должна беспокоиться только о проверке следующего таймера, который истекает, а не весь список.
Грубо говоря, это дает O (log n) для запуска таймера и O (1) для обработки запущенных таймеров.
Редактировать: Просто искал в книге Джеффа Рихтера. Он говорит (о Threading.Timer), что он использует один поток для всех объектов Timer, этот поток знает, когда должен быть установлен следующий таймер (то есть, как указано выше), и вызывает ThreadPool.QueueUserWorkItem для соответствующих обратных вызовов. Это приводит к тому, что если вы не завершите обслуживание одного обратного вызова по таймеру до наступления следующего, ваш обратный вызов будет повторно введен в другой поток пула. Итак, в заключение, я сомневаюсь, что вы столкнетесь с большой проблемой, связанной с большим количеством таймеров, но вы можете испытать истощение пула потоков, если большое количество из них запускается с одним и тем же таймером и / или их обратные вызовы работают медленно.