2015-05-25 3 views
1

Мое приложение получает инструкции от моего клиента.System.Threading callback не кажется точным

В каждой инструкции я хочу выполнить код с принятой отметки времени + 10 секунд.

Чтобы проверить правильность моего кода я высмеивал этот простой тест консоли:

static DateTime d1 = DateTime.Now; 
    static Timer _timer = null; 
    static void Main(string[] args) 
    { 
     _timer = new Timer(Callback, null,200, Timeout.Infinite); 
     d1 = DateTime.Now; 
     Console.ReadLine(); 
    } 

    private static void Callback(Object state) 
    { 
     TimeSpan s = DateTime.Now - d1; 
     Console.WriteLine("Done, took {0} secs.", s.Milliseconds); 
     _timer.Change(200, Timeout.Infinite); 
    } 

На это работает я надеялся на дисплей:

Done, took 200 milsecs. 
Done, took 200 milsecs. 
Done, took 200 milsecs. 
Done, took 200 milsecs. 
Done, took 200 milsecs. 
Done, took 200 milsecs. 

Но это не так.

Я получаю случайные значения для «milsecs».

NB Мой фактический код не всегда будет использовать 200 миллисекунд.

Пример экрана:

enter image description here

+1

Как случайных значений вы получаете? Вы имеете в виду, что он равен 5 миллисекундам, или вы получаете некоторые, которые отключены на 500 миллисекунд? Просьба уточнить. – JRLambert

+1

Есть много таймеров, в которых вы используете Timer? Тем не менее, если вы используете System.Threading.Timer, то переключение потоков означает, что нет гарантии, что она будет выполняться ровно каждые 200 мс - это точность воспроизведения мультимедиа. –

+0

Вы должны использовать класс [StopWatch] (http://stackoverflow.com/questions/457605/how-to-measure-code-performance-in-net), а не DateTime. –

ответ

3

Во-первых, вы используете TimeSpan.Milliseconds свойство, которое является int и возвращает только миллисекунды часть из временного интервала. Для того, чтобы убедиться, что вы получаете полный рабочий день, используйте TotalMilliseconds

Во-вторых, вы не сбросить d1 с каждой петлей, так что вы будете видеть время общего, а не раз в итерацию. Чтобы исправить это, не забудьте установить d1 = DateTime.Now в конце вашего обратного вызова, чтобы следующий цикл использовался в нужное время.

Наконец, вы можете увидеть определенное несоответствие, а не совершенные интервалы в 200 мс, из-за накладных расходов на потоки, вызовы методов и даже операцию вычитания, которую вы делаете. Кроме того, DateTime не всегда имеют наибольшую точность - по this article from Eric Lippert:

Если вопрос, который вы хотите спросить о том, как долго некоторые операции приняли, и вы хотите, высокоточный, с высокой точностью ответ , затем используйте класс StopWatch.

Таким образом:

static Stopwatch _watch = Stopwatch.StartNew(); 
static Timer _timer = null; 
static void Main(string[] args) 
{ 
    _timer = new Timer(Callback, null,200, Timeout.Infinite); 
    Console.ReadLine(); 
} 

private static void Callback(Object state) 
{ 
    TimeSpan s = _watch.Elapsed; 
    _watch.Restart(); 
    Console.WriteLine("Done, took {0} secs.", s.TotalMilliseconds); 
    _timer.Change(200, Timeout.Infinite); 
} 
+0

привет, прочитав ваш ответ сейчас - спасибо –

+0

«Во-вторых, вы не перезагружаете d1 с каждым циклом» - теперь чувствую себя глупо! –

+0

Это улучшило мои результаты потрясающе. Я рассмотрю класс StopWatch. Предполагаю, что я могу использовать обратные вызовы с этим классом? –

1

s.Milliseconds является milliseonds после запятой, так сказать. Вам нужно TotalMilliseconds. Эти свойства названы ужасно. Они являются причиной многих вопросов SO.

Встроенные таймеры должны иметь разрешение около 15 мс (или немного кратного его) и потенциально некоторое искажение (я не знаю).

+0

Я попробую это и другое предложение выше. Спасибо –

+0

Привет, это было проблемой и спасибо за это. Тем не менее, самой большой проблемой был мой плохой код :( –

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