В чем разница между Thread.Sleep (время ожидания) и ManualResetEvent.Wait (время ожидания)?

И Thread.Sleep(timeout), и resetEvent.Wait(timeout) заставляют выполнение приостанавливаться как минимум на timeout миллисекунды, так есть ли между ними разница? Я знаю, что Thread.Sleep заставляет поток отказываться от оставшейся части своего кванта времени, что, возможно, приводит к сну, который длится намного дольше, чем требуется. Есть ли такая же проблема у метода Wait(timeout) объекта ManualResetEvent?

Изменить: я знаю, что основной смысл ManualResetEvent должен передаваться из другого потока — сейчас меня интересует только случай метода ожидания события с указанным тайм-аутом, а не другие вызывающие объекты. установка события. Я хочу знать, надежнее ли просыпаться вовремя, чем Thread.Sleep


person Erik Forbes    schedule 08.06.2010    source источник


Ответы (6)


Thread.Sleep(timeout) вызывает безусловное ожидание перед возобновлением выполнения. resetEvent.WaitOne(timeout) заставляет поток ждать до тех пор, пока (1) не будет инициировано событие или (2) не будет достигнуто время ожидания.

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

РЕДАКТИРОВАТЬ: по времени они оба одинаково надежны. Однако ваш комментарий о "пробуждении вовремя" меня беспокоит. Зачем вам нужен код, чтобы проснуться вовремя? Sleep и WaitOne на самом деле не рассчитаны на точность.

Только если timeout меньше 50 мс или около того и вам нужна надежность, вам следует изучить альтернативные методы синхронизации. Эта статья выглядит довольно хорошим обзором.

person zildjohn01    schedule 08.06.2010
comment
Я знаю это; Меня действительно интересует только случай тайм-аута. Я хочу знать, надежнее ли просыпаться вовремя, чем Thread.Sleep - person Erik Forbes; 08.06.2010
comment
Блокирует ли Thread.Sleep COM-сообщения и события? Если нам нужно задержать основной поток на несколько секунд, что следует использовать Thread.Sleep или WaitOne??? - person lerner1225; 27.07.2017

Основное различие между 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);

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

person Mark Byers    schedule 08.06.2010

Для задержек и периодики я нашел Monitor.Wait хороший выбор..

object timelock = new object();

lock (timelock) { Monitor.Wait(timelock, TimeSpan.FromMilliseconds(X.XX)); }

Это дает превосходный результат... джиттер ~1 мс или лучше, в зависимости от специфики приложения.

Как вы, возможно, уже знаете, Thread.Sleep(X) ненадежен и не может быть отменен... Я избегаю его, как чумы.

person Rusty    schedule 11.06.2010

Функция Sleep() уже давно не работает таким образом. Его точность определяется периодом мультимедийного таймера, который вы можете изменить с помощью P/вызова 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);
}

Вывод на моей машине:

Сон: 999, Ожидание: 1003

с изменчивостью около 5 миллисекунд.

person Hans Passant    schedule 08.06.2010
comment
У вас случайно нет ссылок на это, не так ли? - person Erik Forbes; 10.06.2010

Как уже упоминалось, разница в том, что 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);

Не уверен, что между ними есть разница, но я посмотрю, смогу ли я что-нибудь найти.

person SwDevMan81    schedule 08.06.2010

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

В одном треде вы бы сказали:

    mre.WaitOne(10000); // ten seconds
    Console.WriteLine("Woke up!");

В другом вы бы сказали:

    mre.Set(); // this causes `WaitOne` to return in the first thread

Без вызова Set в другом потоке первый поток фактически заснет на 10 секунд.

person Daniel Earwicker    schedule 08.06.2010