简体   繁体   English

Thread.Sleep(timeout)和ManualResetEvent.Wait(timeout)有什么区别?

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

Both Thread.Sleep(timeout) and resetEvent.Wait(timeout) cause execution to pause for at least timeout milliseconds, so is there a difference between them? Thread.Sleep(timeout)和resetEvent.Wait(timeout)都会导致执行暂停至少timeout毫秒,那么它们之间是否存在差异? I know that Thread.Sleep causes the thread to give up the remainder of its time slice, thus possibly resulting in a sleep that lasts far longer than asked for. 我知道Thread.Sleep导致线程放弃其时间片的剩余部分,因此可能导致睡眠持续时间远远超过要求的时间。 Does the Wait(timeout) method of a ManualResetEvent object have the same problem? ManualResetEvent对象的Wait(timeout)方法是否具有相同的问题?

Edit : I'm aware that a ManualResetEvent's main point is to be signaled from another thread - right now I'm only concerned with the case of an event's Wait method with a timeout specified, and no other callers setting the event. 编辑 :我知道一个ManualResetEvent的主要要点是从另一个线程发出信号 - 现在我只关注事件的Wait方法的情况,指定了超时,没有其他调用者设置事件。 I want to know whether it's more reliable to awaken on-time than Thread.Sleep 我想知道是否比Thread.Sleep更准确地唤醒准时

Thread.Sleep(timeout) causes an unconditional wait before execution is resumed. Thread.Sleep(timeout)在执行恢复之前导致无条件等待。 resetEvent.WaitOne(timeout) causes the thread to wait until either (1) the event is triggered, or (2) the timeout is reached. resetEvent.WaitOne(timeout)导致线程等待,直到触发事件(1)或(2)达到超时。

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. SleepWaitOne在设计时并没有精确设计。

Only if timeout is below 50ms or so and you need the reliability , you should look into alternate methods of timing. 只有当timeout低于50ms左右并且您需要可靠性时 ,您才应该研究其他的计时方法。 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. Thread.SleepManualResetEvent.WaitOne之间的主要区别在于,您可以使用Set方法向等待ManualResetEvent的线程发出信号,从而导致线程在超时之前唤醒。

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: 从.NET Reflector我可以看到方法ManualResetEvent.WaitOne最终导致使用以下签名调用extern方法:

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

Whereas Thread.Sleep calls this extern method: Thread.Sleep调用这个extern方法:

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.. 对于延迟和周期,我发现Monitor.Wait是个不错的选择..

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. 这可以提供出色的结果....〜1ms抖动或更好,具体取决于具体应用。

As you may already know Thread.Sleep(X) is unreliable and cannot be canceled....I avoid it like the plague. 你可能已经知道Thread.Sleep(X)是不可靠的,不能取消....我像瘟疫一样避免它。

The Sleep() function hasn't worked this way for a long time. Sleep()函数在很长一段时间内都没有这种方式。 Its accuracy is determined by the multimedia timer period, something you can change by P/Invoking timeBeginPeriod(). 它的准确性由多媒体计时器周期决定,你可以通过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. 不幸的是,在我的机器上,我有一些程序将这段时间设置为1毫秒,使睡眠精确到毫秒。 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 睡眠:999,等待:1003

with a variability of about 5 milliseconds. 可变性约为5毫秒。

As others have mentioned, the difference is WaitOne could return before the sleep time if signaled. 正如其他人所提到的,不同之处在于,如果发出信号,WaitOne可能会在睡眠时间之前返回。 Sleep is guaranteed to wait for the sleep time. 保证睡眠等待睡眠时间。

Thread.Sleep in reflector calls: Thread.Sleep在反射器调用中:

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

ManualResetEvent.Wait in reflector calls: 反射器调用中的ManualResetEvent.Wait:

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. 如果没有在另一个线程中调用Set ,第一个线程将有效地休眠10秒。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM