Оба Потока. Сон (тайм-аут) и resetEvent. Ожидайте (тайм-аут) выполнение причины для приостановки для, по крайней мере, timeout
миллисекунды, так там различие между ними? Я знаю тот Поток. Сон заставляет поток бросать остаток от своего интервала времени, таким образом возможно приводящего ко сну, который длится намного дольше, чем попросивший относительно. Ожидание (тайм-аут) метод объекта ManualResetEvent имеют ту же проблему?
Править: Я знаю, что основной момент ManualResetEvent должен быть сообщен от другого потока - прямо сейчас я только обеспокоен случаем метода Ожидания события с тайм-аутом, указанным, и никакие другие вызывающие стороны, устанавливающие событие. Я хочу знать, более ли это надежно для пробуждения вовремя, чем Поток. Сон
Thread.Sleep (timeout)
вызывает безусловное ожидание перед возобновлением выполнения. resetEvent.WaitOne (timeout)
заставляет поток ждать, пока (1) событие не сработает, либо (2) не истечет время ожидания.
Смысл использования событий состоит в том, чтобы запускать их из другого потока, чтобы вы могли напрямую контролировать, когда поток просыпается. Если вам это не нужно, вы не должны использовать объекты событий.
РЕДАКТИРОВАТЬ: С точки зрения времени, они оба одинаково надежны. Однако ваш комментарий о «своевременном пробуждении» меня беспокоит. Зачем нужен код, чтобы вовремя просыпаться? Sleep
и WaitOne
на самом деле не рассчитаны на точность.
Только если тайм-аут
меньше 50 мс или около того и вам нужна надежность , вам следует изучить альтернативные методы отсчета времени. Эта статья выглядит неплохим обзором.
Как уже упоминалось, разница в том, что WaitOne может вернуться до времени ожидания, если будет получен сигнал. Сон гарантированно дождется времени сна.
Thread.Sleep в вызовах рефлектора:
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SleepInternal(int millisecondsTimeout);
ManualResetEvent.Wait в вызовах рефлектора:
private static extern int WaitOneNative(SafeWaitHandle waitHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);
Не уверен, есть ли разница между ними, но я посмотрю, смогу ли я что-нибудь найти.
Функция Sleep() уже давно не работает таким образом. Ее точность определяется периодом мультимедийного таймера, который можно изменить с помощью P/Invoking timeBeginPeriod(). К сожалению, на моей машине есть программа, которая устанавливает этот период равным одной миллисекунде, что делает сон точным до миллисекунды. Вот код, который вы можете попробовать сами:
using System;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
//timeBeginPeriod(1);
var sw1 = Stopwatch.StartNew();
for (int ix = 0; ix < 100; ++ix) Thread.Sleep(10);
sw1.Stop();
var sw2 = Stopwatch.StartNew();
var mre = new ManualResetEvent(false);
for (int ix = 0; ix < 100; ++ix) mre.WaitOne(10);
sw1.Stop();
Console.WriteLine("Sleep: {0}, Wait: {1}", sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
Console.ReadLine();
//timeEndPeriod(1);
}
[DllImport("winmm.dll")]
private static extern int timeBeginPeriod(int period);
[DllImport("winmm.dll")]
private static extern int timeEndPeriod(int period);
}
Вывод на моей машине:
Sleep: 999, Wait: 1003
с вариативностью около 5 миллисекунд.
Для задержек и периодики я нашел Monitor.Wait хорошим выбором...
object timelock = new object();
lock (timelock) { Monitor.Wait(timelock, TimeSpan.FromMilliseconds(X.XX)); }
Это дает отличный результат....~1ms jitter или лучше, в зависимости от специфики приложения.
Как вы, возможно, уже знаете, Thread.Sleep(X) ненадежен и не может быть отменен.... Я избегаю его как чумы.
Спящий режим продолжается указанное время. Ожидание события может закончиться раньше, если о событии будет сообщено. Это цель событий: позволить одному потоку дать команду другому проснуться.
В одном потоке вы бы сказали:
mre.WaitOne(10000); // ten seconds
Console.WriteLine("Woke up!");
В другом вы бы сказали:
mre.Set(); // this causes `WaitOne` to return in the first thread
Без вызова Set
в другом потоке первый поток фактически спал бы в течение 10 секунд.
Основное различие между Thread.Sleep
и ManualResetEvent.WaitOne
заключается в том, что вы можете подать сигнал потоку, ожидающему ManualResetEvent, используя метод Set, что заставит поток проснуться раньше таймаута.
Если вы не подаете сигнал, то я ожидаю, что они будут вести себя очень похоже.
Из .NET Reflector я вижу, что метод ManualResetEvent.WaitOne
в конечном итоге приводит к вызову внешнего метода со следующей сигнатурой:
int WaitOneNative(SafeWaitHandle waitHandle,
uint millisecondsTimeout,
bool hasThreadAffinity,
bool exitContext);
Тогда как Thread.Sleep
вызывает этот внешний метод:
void SleepInternal(int millisecondsTimeout);
К сожалению, у меня нет исходного кода этих методов, поэтому я могу только догадываться. Я бы предположил, что оба вызова приводят к тому, что поток выходит из расписания, ожидая истечения тайм-аута, при этом ни один из них не является более точным, чем другой.