2013-11-01 4 views
1

Здесь у меня странное поведение в моем тестовом коде, код довольно прост, создается куча таймеров с одинаковым шаблоном: добавьте Thread.Sleep во весь обратный вызов. Затем запустите таймеры почти в одно и то же время, затем я вижу, что обратный вызов таймера был с задержкой.Thread.Sleep вызывает задержку обратного вызова таймера

public class StrangTimerTesting 
{ 
    public int callback_EnteredTimes; 

    // for avoid timers get GCed. 
    private List<System.Timers.Timer> timerContainer = new List<System.Timers.Timer>(); 

    public void Go() 
    { 
     for (var i = 0; i < 5; i++) 
     { 
      var displayTimer = new System.Timers.Timer(1000); 
      displayTimer.Elapsed += (a, b) => 
      { 
       Interlocked.Increment(ref this.callback_EnteredTimes); 
       var initalTime = DateTime.Now.ToString("HH:mm:ss.ffff"); 
       Console.WriteLine("entered times: " + callback_EnteredTimes + ", [email protected]" + initalTime); 
       displayTimer.Stop(); 

       // why this Sleep cause some callback delayed to be called???? 
       Thread.Sleep(6000); 
      }; 

      displayTimer.Start(); 
      timerContainer.Add(displayTimer); 
     } 
    } 
} 

Я полагаю, все обратного вызова будет называться почти то же самое время, хотя на другом потоке пула потоков, но результат тестирования явно не поддерживают это, там уже некоторые 2 секунды разрыв, если я извлекал что Thread.Sleep, тогда все в порядке. Может кто-нибудь указать на причину?

EDIT1: это результат из программы тестирования:

* Введенные раз: 1, intial @ 08: 43: 29,4732

вступил раз: 2, intial @ 08: 43: 29,4762

введенные раз: 3, intial @ 08: 43: 30,4763

вступил раз: 4, intial @ 08: 43: 30,9764

вошел раз: 5, intial @ 08: 43: 31,4764 *

И рассчитывать MinThreads в моем ноутбуке 2.

+0

Всегда задерживается или первый раз? –

+0

@Sriram: первые два обратных вызова всегда вызываются в то время, а последние всегда задерживаются, это мой результат теста: введенное время: 1, intial @ 16: 48: 53.6435 введенное время: 2, intial @ 16: 48: 53.6435 введенное время: 3, intial @ 16: 48: 54.6896 введенное время: 4, intial @ 16: 48: 55.1436 введенное время: 5, intial @ 16: 48: 55.6437 – Shawn

+0

Сколько у вас ядер? Пробовали ли вы настройку 'ThreadPool.SetMinThreads' –

ответ

7

Вы создали проблему пожаротушения . У вас есть 5 таймеров, каждый из которых занимает отметку 1 секунду, чей обработчик события Elapsed спящий на 6 секунд. Вы надеетесь, что ваша программа по сути добавит 5 потоков каждую секунду, которые потребуют 30 секунд, чтобы выполнить свою работу. Или по-другому, каждую минуту он добавляет 300 потоков и завершает только 10 из них.

Если вы не уверены, это не будет хорошим. Мягко говоря. Поток - очень дорогой ресурс операционной системы. За 5 ручками он потребляет мегабайт виртуальной памяти. В 32-разрядном процессе программа занимает всего 7 минут, чтобы потреблять всю доступную память и сбой OutOfMemoryException.

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

Это задание планировщика ThreadPool. Он пытается сохранить количество выполняемых потоков ТТ до установленного минимума. На вашей машине минимум равен 2, количество ядер, которые у вас есть. Если запустить более 2, это не имеет особого смысла, операционная система должна будет сделать больше работы, чтобы дать этим потокам возможность запускаться, переключаясь между ними. Фактически, замедление их, идеальное число - 2, поэтому у них есть хороший шанс получить неограниченный доступ к процессору и завершить свою работу как можно быстрее.

Планировщик ThreadPool однако не знает, что делают потоки, он имеет очень несовершенное знание кода, который они выполняют. Он не знает, что ваши потоки вообще не выполняют какую-либо работу и просто спят. Для этого есть контрмера. Дважды в секунду он переопределяет свою политику планирования по умолчанию, если активные потоки не завершаются, это позволяет начать поток. Это продолжается, если это абсолютно необходимо, до максимально допустимого. Очень большое число, это должно быть около 500 на вашей машине.

Это довольно эффективный алгоритм, который предотвратит взлом вашей программы через 7 минут. Он все еще взрывается, но это займет очень много времени. В конечном итоге вы все еще получаете OOM от многомиллионных запросов на загрузку TP, которые планировщик TP не может обслуживать. В противном случае просто необоснованный результат, который вам всегда нужно ожидать, когда вы пишете необоснованную программу.

+0

спасибо за подробности в ThreadPool, и я уточню программу, это просто для тестирования и что 'Go()' будет вызываться только один раз. затем посмотрите на мой результат (я просто переделаю и добавлю его), третий ввод имеет 1-й промежуток до первого и второго, в то время как четвертый имеет 0,5-секундный промежуток до третьего, вряд ли тот, который вы упомянули «Два раза в секунду», мог бы объяснить? – Shawn

1

От ThreadPool docs on MSDN:

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

Я подозреваю, что минимальное значение для пула потоков по умолчанию равно 2, потому что ваша система имеет 2 ядра. Структура добавит потоки в идентификатор threadpool, там есть работа, но все потоки пулов в данный момент заняты. Однако, как упоминалось в документах, инфраструктура может немного подождать (и, скорее всего, это происходит), предполагая, что рабочий поток в использовании, возможно, скоро станет доступен.

Вы можете вывести минимальный счетчик потоков, используя метод ThreadPool.GetMinThreads().

+0

Кажется, вы правы, но мне просто интересно, почему создать новый поток в Pool стоить много времени (2 секунды)? – Shawn

Смежные вопросы