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

时间:2010-06-08 16:06:46

标签: c# multithreading timeout sleep waithandle

Thread.Sleep(timeout)和resetEvent.Wait(timeout)导致执行暂停至少timeout毫秒,那么它们之间有区别吗?我知道Thread.Sleep导致线程放弃其时间片的剩余部分,因此可能导致睡眠持续时间远远超过要求的时间。 ManualResetEvent对象的Wait(timeout)方法是否有同样的问题?

编辑:我知道要从另一个线程发出一个ManualResetEvent的主要信号 - 现在我只关注指定超时的事件Wait方法的情况,并且没有其他呼叫者设置该事件。我想知道是否比Thread.Sleep

更准确地唤醒准时

6 个答案:

答案 0 :(得分:21)

在恢复执行之前,

Thread.Sleep(timeout)会导致无条件等待。 resetEvent.WaitOne(timeout)导致线程等待,直到(1)触发事件,或(2)达到超时。

使用事件的目的是从另一个线程触发它们,这样你就可以直接控制线程何时被唤醒。如果您不需要,则不应使用事件对象。

编辑:时间方面,他们都同样可靠。但是,你对“按时觉醒”的评论让我很担心。为什么需要按时唤醒代码? SleepWaitOne并未真正考虑精确度。

仅当timeout低于50毫秒且您需要可靠性时,您应该研究其他的计时方法。 This article看起来非常好。

答案 1 :(得分:8)

Thread.SleepManualResetEvent.WaitOne之间的主要区别在于您可以使用Set方法向等待ManualResetEvent的线程发出信号,导致线程比超时更早地唤醒。

如果你没有发出信号,那么我希望它们的表现方式非常相似。

从.NET Reflector我可以看到方法ManualResetEvent.WaitOne最终导致调用带有以下签名的extern方法:

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

Thread.Sleep调用此外部方法:

void SleepInternal(int millisecondsTimeout);

不幸的是我没有这些方法的源代码,所以我只能猜测。我想,在两次调用中都会导致线程在等待超时时间之前被调度出来,并没有比另一次更精确。

答案 2 :(得分:6)

对于延迟和周期,我发现Monitor.Wait是一个不错的选择..

object timelock = new object();

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

这给出了一个很好的结果....〜1ms抖动或更好,具体取决于应用程序的具体情况。

你可能已经知道Thread.Sleep(X)是不可靠的,无法取消....我就像瘟疫一样避免它。

答案 3 :(得分:4)

Sleep()函数在很长一段时间内都没有这种方式。它的准确性由多媒体计时器周期决定,你可以通过P / Invoking timeBeginPeriod()来改变它。不幸的是,在我的机器上,我有一些程序将这段时间设置为1毫秒,使睡眠精确到毫秒。以下是一些自己尝试的代码:

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毫秒。

答案 4 :(得分:2)

正如其他人所提到的,不同之处在于,如果发出信号,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);

不确定两者之间是否存在差异,但我会看是否能找到一些东西。

答案 5 :(得分:1)

睡眠持续指定的时间。如果发出事件信号,事件等待可以更快结束。这是事件的目的:允许一个线程告诉另一个线程唤醒。

在一个帖子中你会说:

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

在另一个你会说:

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

如果没有在另一个线程中调用Set,第一个线程将有效地休眠10秒。