15

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

编辑:我知道 ManualResetEvent 的要点是从另一个线程发出信号 - 现在我只关心指定超时的事件等待方法的情况,而没有其他调用者设置事件。我想知道按时唤醒是否比Thread.Sleep更可靠

4

6 回答 6

27

Thread.Sleep(timeout)在恢复执行之前导致无条件等待。resetEvent.WaitOne(timeout)导致线程等待,直到 (1) 事件被触发,或 (2) 达到超时。

使用事件的重点是从另一个线程触发它们,因此您可以直接控制线程何时唤醒。如果你不需要这个,你不应该使用事件对象。

编辑:在时间方面,它们都同样可靠。但是,您关于“准时醒来”的评论让我担心。为什么你需要你的代码按时唤醒?Sleep并且WaitOne在设计时并没有真正考虑到精确性。

只有当timeout低于 50ms 左右并且您需要可靠性时,您才应该研究替代的计时方法。这篇文章看起来是一个很好的概述。

于 2010-06-08T16:09:43.143 回答
10

Thread.Sleep和之间的主要区别在于,您可以使用SetManualResetEvent.WaitOne方法向等待 ManualResetEvent 的线程发出信号,从而导致线程在超时之前唤醒。

如果您不发出信号,那么我希望他们的行为方式非常相似。

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

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

Thread.Sleep调用这个外部方法:

void SleepInternal(int millisecondsTimeout);

不幸的是,我没有这些方法的源代码,所以我只能猜测。我想在这两个调用中都会导致线程在等待超时到期时被调度出来,而且两者都没有比另一个更准确。

于 2010-06-08T16:09:26.810 回答
7

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

object timelock = new object();

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

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

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

于 2010-06-11T16:52:17.333 回答
5

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

我机器上的输出:

睡眠:999,等待:1003

具有大约 5 毫秒的可变性。

于 2010-06-08T17:09:59.183 回答
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);

不确定两者之间是否有区别,但我会看看我是否能找到一些东西。

于 2010-06-08T16:12:28.657 回答
1

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

在一个线程中,您会说:

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

在另一个你会说:

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

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

于 2010-06-08T16:10:10.100 回答