What is the difference between Thread.Sleep(timeout) and ManualResetEvent.Wait(timeout)?

一曲冷凌霜 提交于 2019-11-28 20:49:05

Thread.Sleep(timeout) causes an unconditional wait before execution is resumed. resetEvent.WaitOne(timeout) causes the thread to wait until either (1) the event is triggered, or (2) the timeout is reached.

The point of using events is to trigger them from another thread, so you can directly control when the thread wakes up. If you don't need this, you shouldn't be using event objects.

EDIT: Timing-wise, they are both equally reliable. However, your comment about "awakening on time" worries me. Why do you need your code to wake up on time? Sleep and WaitOne aren't really designed with precision in mind.

Only if timeout is below 50ms or so and you need the reliability, you should look into alternate methods of timing. This article looks like a pretty good overview.

The main difference between Thread.Sleep and ManualResetEvent.WaitOne is that you can signal to a thread waiting on a ManualResetEvent using the Set method, causing the thread to wake up earlier than the timeout.

If you don't signal then I would expect them to behave in a very similar way.

From .NET Reflector I can see that the method ManualResetEvent.WaitOne eventually results in a call to an extern method with the following signature:

int WaitOneNative(SafeWaitHandle waitHandle,
                  uint millisecondsTimeout,
                  bool hasThreadAffinity,
                  bool exitContext);

Whereas Thread.Sleep calls this extern method:

void SleepInternal(int millisecondsTimeout);

Unfortunately I don't have the source code for these methods, so I can only guess. I'd imagine that in both calls result in the thread getting scheduled out while it is waiting for the time out to expire, with neither being particularly more accurate than the other.

For delays and periodics I have found Monitor.Wait a good choice..

object timelock = new object();

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

This gives a excellent result....~1ms jitter or better depending on application specifics.

As you may already know Thread.Sleep(X) is unreliable and cannot be canceled....I avoid it like the plague.

The Sleep() function hasn't worked this way for a long time. Its accuracy is determined by the multimedia timer period, something you can change by P/Invoking timeBeginPeriod(). Unfortunately, on my machine I've got some kind of program that sets this period to one millisecond, making sleeps accurate down to a millisecond. Here's some code to try for yourself:

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);
}

Output on my machine:

Sleep: 999, Wait: 1003

with a variability of about 5 milliseconds.

As others have mentioned, the difference is WaitOne could return before the sleep time if signaled. Sleep is guaranteed to wait for the sleep time.

Thread.Sleep in reflector calls:

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SleepInternal(int millisecondsTimeout);

ManualResetEvent.Wait in reflector calls:

private static extern int WaitOneNative(SafeWaitHandle waitHandle, uint millisecondsTimeout, bool hasThreadAffinity, bool exitContext);

Not sure if there is a difference between the two, but I'll see if I can find something.

The Sleep continues for the specified time. The event wait can end sooner if the event is signalled. This is the purpose of events: to allow one thread to tell another to wake up.

In one thread you'd say:

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

In another you'd say:

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

Without the call to Set in the other thread, the first thread would effectively sleep for 10 seconds.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!