安全地暂停线程[重复]

Posted

技术标签:

【中文标题】安全地暂停线程[重复]【英文标题】:Pausing a thread safely [duplicate] 【发布时间】:2019-04-24 06:43:54 【问题描述】:

我想知道如果我想在每次迭代时将线程暂停一段定义的时间是否有任何问题(我正在运行一个连续循环)。 我的第一选择是使用Task.Delay,但我不知道是否有任何问题。我应该选择Thread.Sleep 还是EventWaitHandle

class UpdateThread 

        private  Thread thread;
        Fabric.Client client;
        public UpdateThread(Fabric.Client client) 

        
        public void Run() 
            thread = new Thread(new ThreadStart(async()=>await UpdateAsync()));
        
        public async Task UpdateAsync() 

            while (true) 
                await Task.Delay(Constants.REFRESH_INTERVAL);

            

        

    

上述方法的缺点是什么?

P.S:此线程与Windows Forms 应用程序(线程)一起运行

【问题讨论】:

我在这里要小心,Thread.Sleep 通常表明您正在等待其他事情完成,除非其他事情是确定性的(在 dot net 中可能不是),这可能会引入错误。或者你最终会暂停你的线程超过必要的时间(以避免提前唤醒它。看看这个blogs.msmvps.com/peterritchie/2007/04/26/… 我倾向于Task.Delay。它有什么问题吗?我只想暂停以下代码的执行一段定义的时间。 【参考方案1】:

传递给Thread 的构造函数的ThreadStart 委托存在潜在问题,该构造函数定义为public delegate void ThreadStart()。您为其提供了一个async void lambda 的事实使它成为一个即发即弃的调用。即,它是异步的,但它不返回 Task 来观察结果或异常。

你的新线程很可能会在它内部的执行流到达第一个await somethingawait Task.Delay 或其他任何东西时结束。因此,从技术上讲,您不会在此处暂停线程await 之后的逻辑执行将在随机线程池线程上继续,这很可能与您最初创建的线程不同。

您最好只使用Task.Run 而不是new Thread。前者有an override 用于async Task lambdas,无论如何你通常应该使用它而不是async void。因此,您可以将 UpdateAsync 直接传递给 Task.Run 并使用 proper exception propagation logic 用于异步方法。

如果出于某种原因您仍想坚持使用new Thread 并将async void lambda 传递给它,请确保观察UpdateAsync 引发的所有异常。否则,它们将在随机池线程上被“带外”抛出,有关更多详细信息,请参见上面的链接。另请注意,创建一个新线程(然后几乎立即结束它)是一项相当昂贵的运行时操作。 OTOH,当使用Task.Run 时,您通常只需从线程池借用/返回现有线程即可,这要快得多。

也就是说,在这种特殊情况下,您最好只使用Thread.Sleep 而不是async 方法和Task.Delay,以避免处理异步和线程切换。它是一个客户端 WinForms 应用程序,您通常(在合理范围内)不关心缩放,即繁忙或阻塞线程的数量。

【讨论】:

好吧,我不在乎它是 Task 还是 Thread,只要它与 Main thread 分离即可。事实上它的执行由不同的线程完成(@987654348 @) 不会打扰我,只要它的执行在每次迭代中都有一个逻辑暂停。这个Task/Thread 将充当后台工作人员,并将具有应用程序的范围。 @BercoviciAdrian,那你为什么要发起一个话题呢?您可以将await Task.Delay(REFRESH_INTERVAL).ConfigureAwait(false) 之类的操作作为UpdateAsync 的第一条语句。 @BercoviciAdrian 是的,你可以而且你已经做到了,在 UpdateAsync 内。 如果异步方法中有 CPU 密集型或阻塞代码,您只需将异步方法卸载到另一个线程(通常使用 Task.Run)。通常,只有在 UI 线程上调用类似的异步方法时,这才是一个问题。 @BercoviciAdrian,完全没问题,IMO 那是正确的方法。只需注意异常处理/日志记录,具体取决于该任务的性质。搜索“任务+即发即弃”,这似乎是你的场景。【参考方案2】:

在这种情况下,您应该使用Task.Delay,因为Thread.Sleep 会从 .NET 线程池发送一个线程进入睡眠状态,这很可能不是您想要的。您还将较低级别的Thread 与较高级别的Task 混合在一起。您无需启动新线程。只需拨打UpdateAsync() 即可,无需拨打Wait() 或类似名称。

【讨论】:

"Thread.Sleep 会从 dot net 线程池发送一个线程进入睡眠状态"。睡眠的工作不是让当前线程进入睡眠状态(不管是线程池线程)。 Thread.Sleep 将当前线程设置为睡眠状态,但可能会在 .NET 线程池中的线程上安排异步方法,在这种情况下,它将被设置为睡眠状态。 所以基本上执行 UpdateAsync 的线程在这两种情况下都会进入休眠状态? 是的,但Thread.Sleep 不会通知TaskScheduler,因此它认为线程仍在忙于执行您的方法。【参考方案3】:

当你想阻塞当前线程时使用Thread.Sleep

如果您想要一个逻辑延迟而不阻塞当前线程,请使用Task.Delay

Source

我更喜欢使用Thread.Sleep 处理此类情况,因为它在我的脑海中级别较低且更有效,但这只是个人的事情。

【讨论】:

以上是关于安全地暂停线程[重复]的主要内容,如果未能解决你的问题,请参考以下文章

暂停功能,直到承诺解决[重复]

关于servlet的线程安全[重复]

在Java中测试线程安全[重复]

可变关键字线程安全[重复]

如何测试线程安全[重复]

为啥Servlet中的实例变量不是线程安全的[重复]