让当前线程休眠

15

我有一个在线程中(非主线程)执行的工作单元。在某些情况下,我想让该线程休眠10秒钟。Thread.Sleep(10000)是最节约资源的方法吗?


5
你确定要让线程睡眠吗?这是一种糟糕的代码味道。首先,在休眠期间,该线程不会响应来自其他线程的事件。其次,十秒钟相当于四百亿个处理器周期。你真的想要浪费一个整个线程的四百亿个处理器周期吗?在那段时间里它不能做其他事情吗? - Eric Lippert
6
在这种情况下,为什么要花费资源来创建一个完整的线程呢?我会在主线程上完成工作;当你需要等待时,设置一个计时器,然后随着工作的到来继续在主线程上工作。最终计时器会到期并且主线程将继续执行工作。.NET中的线程非常重量级 -- 每当我看到一个线程处于空闲状态时,都会感到担忧。你分配了那个线程 -- 具有百万字节的地址空间 -- 来执行工作,所以让它保持工作状态。 - Eric Lippert
2
请注意,这就是异步 CTP 中“延迟”功能的工作原理。当前线程不会被挂起,它会继续执行任务。当延迟时间到达时,延迟的后续操作将被加入到线程的工作队列中。这样就可以实现所需的延迟,而无需浪费一个空闲线程。 - Eric Lippert
2
@Martin James:你是在固件上做这个吗?我从事过程控制软件方面的工作,如果我看到有人这样使用sleep函数,那么它肯定会被标记为一个问题。 - Greg D
3
你有两辆车和三名司机。其中两名司机经常争抢一辆车使用权。你的说法是:“如果我付给其中一名司机睡觉的工资,他们就不会再为使用这辆车而争执了。” 但你现在是在花钱让一名司机睡觉。这才是问题所在!如果你只需要两名司机工作,那么只付两名司机的工资并让他们各开一辆车即可。如果你需要三名司机工作,那么买下第三辆车。然而,花费昂贵的司机工资来让他闲置一年则是对资源的浪费。 - Eric Lippert
显示剩余5条评论
6个回答

24

Thread.Sleep(10000)是最节约资源的方法吗?

从不忙等待而是让出CPU的角度来看,确实是最节约资源的。

但这种方式会浪费一个线程。如果有很多休眠线程,就不应采用此方法进行扩展。


7
请注意,在C# 5中,使用await Task.Yield可以轻松放弃当前线程10秒钟,包括取消操作。 - Jon Skeet
有太多睡眠线程会有什么影响?在我写这篇文章的时候,我的计算机有1007个线程和2%的CPU使用率。显然,几乎所有这些线程都被阻塞在某些东西上,可能是睡眠或者被阻塞在带有超时的东西上(因此也在操作系统的超时队列中)。看起来并没有对性能产生太大影响。 - Martin James
2
@Martin: .NET线程会立即使用1 MB的堆栈。您看到的1000个线程来自不同的进程,并且许多是未受管理的,具有较小的堆栈。通常情况下,您无法在.NET应用程序中创建1k个线程。几百个已经很多了。 - H H
6
@MartinJames:你没有看到正确的资源。 CPU 是无关紧要的;1000 个休眠的线程消耗十亿字节的地址空间(当然是分布在多少进程中)。如果它是 .NET 线程,则该地址空间实际上已经提交到页面文件中 - Eric Lippert
@EricLippert 等等。哦 - 不喜欢.NET的另一个原因<g>我的1000多个线程中有很多似乎属于操作系统和M$提供的应用程序 - 感谢上帝,M$没有为自己的东西使用.NET,否则我会被淹没!然而,更严肃的是,对于只需要3个或20个异步流程的要求,每个流程一个线程是一个合理的解决方案,如果进程需要10秒的等待,那么好吧,我会sleep()。主线程计时器或其他类似的东西只会增加很多额外的麻烦,没有明显的收益,(好吧,即使是我也会对数百个线程感到不安,无论是.NET还是其他)。 - Martin James
2
显然,现在在你链接到Penny Arcade漫画中七月份的部分需要使用07。http://www.penny-arcade.com/comic/2002/07/22 ;^) - ruffin

14

由于没有其他人提到过这一点...

如果你想让另外一个线程能够唤醒你的“睡眠”线程,你很可能需要使用 Monitor.Wait 而不是 Thread.Sleep

private readonly object sharedMonitor;
private bool shouldStop;

public void Stop()
{
    lock (sharedMonitor)
    {
        shouldStop = true;
        Monitor.Pulse(sharedMonitor);
    }
}

public void Loop()
{
    while (true)
    {
        // Do some work...

        lock (sharedMonitor)
        {
            if (shouldStop)
            {
                return;
            }
            Monitor.Wait(sharedMonitor, 10000);
            if (shouldStop)
            {
                return;
            }
        }
    }
}

注意我们只在锁内部访问shouldStop,因此没有任何内存模型方面的问题。

可能希望循环等待,直到你真正休眠了10秒,以防止发生虚假唤醒——这取决于你再次执行工作的间隔时间有多重要。不要再次执行该工作的重要性如何。(我从未知道过虚假唤醒,但我相信它们是可能存在的。)


1
或者为什么不使用ManualResetEvent?它与之类似但代码更短... - erikH
@erikH:通常我使用CLR锁构造,除非我真的需要Win32事件。 - Jon Skeet
你使用lock而不是Monitor.Enter()的原因是什么? - Erik Philips
阅读这篇文章确实很有趣,因为它在可读性方面有些半不一致(对于所有人来说可能并不明显,特别是在我当时),通过混合语句和静态类方法,但是在阅读了“Monitor vs lock”之后,我肯定理解了你的评论。 - Erik Philips
@Erik:不太确定您所指的“半不一致”,它只是语法糖,就像 foreach 和 using 一样。它调用静态方法的事实既非此处也非彼处,在我看来。 - Jon Skeet
1
@Jon,就可读性而言,“lock”然后调用静态方法“Monitor.Wait()”是(在这里肯定是挑剔)不一致的,但从功能角度来看显然更优。基本上我是在说你本可以使用“Monitor.Enter()”,所有锁定都将一致可读(在我看来),但出于明显的好原因(我很欣赏),你没有这样做。 - Erik Philips

8

养成使用Thread.CurrentThread.Join(timeout) 而不是 Thread.Sleep 的习惯。

区别在于 Join 仍会执行一些消息泵(例如GUI和COM)。

大部分情况下这并不重要,但如果你需要在应用程序中使用一些COM或GUI对象,这将使生活更加轻松。


1

这将每 x 秒处理一些内容,而不使用线程。不确定如何与每两秒创建一个任务的方式相比。

public void LogProcessor()
    {
       if (_isRunning)
        {
            WriteNewLogsToDisk();              
            // Come back in 2 seonds
            var t = Task.Run(async delegate
            {
                await Task.Delay(2000);
                LogProcessor();
            });               
        }
    }

1

从资源效率的角度来看,是的。

对于设计而言,取决于暂停的情况。您希望您的工作是自主的,因此如果线程必须暂停,因为它知道要等待,则使用静态Thread.Sleep方法在线程代码中放置暂停。如果暂停发生是由于某些其他外部事件,则需要控制线程处理,然后让线程所有者保留对线程的引用并调用childThread.Sleep。


0

是的。没有其他有效或安全的方法来使线程休眠。

但是,如果您正在循环中执行某些工作,则可能希望在循环中使用Sleep,以使取消线程更容易,以防您想要取消工作。

以下是一个示例:

bool exit = false;
...
void MyThread()
{
    while(!exit)
    {
        // do your stuff here...
        stuff...
        // sleep for 10 seconds
        int sc = 0;
        while(sc < 1000 && !exit) { Thread.Sleep(10); sc++; }
    }
}

只是要注意,exit 应该是一个全局变量,可以从任何地方查看,并且您可以将其设置为 true 以退出线程。显然还有其他要考虑的事情。例如,您永远不应该在 UI 线程中使用 Sleep。 - Polynomial
1
在这里使用Monitor.Wait会更有效率 - 你可以等待10秒钟,如果有什么东西想要唤醒你告诉你退出,它们可以脉冲监视器。 - Jon Skeet
(鉴于我的回答),我也会质疑“没有其他有效或安全的方法来使线程休眠”的说法... - Jon Skeet
你需要将exit声明为volatile或使用其他方法来防止它被缓存。 - H H
Jon - 谢谢你的信息,我没有想过用那种方式来做。我会重构我的现有项目来使用它。 - Polynomial

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接