【问题标题】:Would looping Thread.Sleep() be bad for performance when used to pause a thread?当用于暂停线程时,循环 Thread.Sleep() 会对性能不利吗?
【发布时间】:2016-11-27 12:25:23
【问题描述】:

关于使用Thread.Sleep() 方法是好是坏的讨论很多。据我了解,它主要用于调试目的。

现在我想知道:用于我的特定目的是否不好,即不断循环它以能够暂停/恢复线程?我这样做是因为我想暂停执行 I/O 操作的线程并能够以简单的方式恢复它。

I/O 操作基本上只是将 4096 字节的块写入文件,直到所有数据都写入文件。由于文件可能很大并且需要很长时间,我希望能够暂停操作(以防它开始占用大量系统资源)。

我的代码,VB.NET 版本:

'Class level.
Private BytesWritten As Long = 0
Private Pause As Boolean = False

'Method (thread) level.
While BytesWritten < [target file size]
    ...write 4096 byte buffer to file...

    While Pause = True
        Thread.Sleep(250)
    End While

    ...do some more stuff...
End While

C# 等效项:

//Class level.
long bytesWritten = 0;
bool pause = false;

//Method (thread) level.
while(bytesWritten < [target file size]) {
    ...write 4096 byte buffer to file...

    while(pause == true) {
        Thread.Sleep(250);
    }

    ...do some more stuff...
}

我听说过 ResetEvents,并且对它们的作用略知一二,但我从未真正深入研究过它们。

【问题讨论】:

  • 我会使用手动重置事件。在每个块之后,等待事件。要暂停线程,请重置事件。要恢复线程,请设置事件。
  • 我不明白你为什么要暂停一个长时间的操作。当然,这只会使它花费更长的时间。如果您希望文件异步写入,那么为什么不使用异步文件 IO 并等待结果呢?
  • @EricLippert :如果我的应用程序的用户想要暂停它,因为该操作可能会占用大量系统资源。

标签: c# vb.net multithreading blocking thread-sleep


【解决方案1】:

我认为更优雅的方法是让线程无限期地休眠,直到它被另一个线程在第一个休眠的线程上调用 Thread.Interrupt 唤醒。示例代码中有一个很好的示例:Pausing and Resuming Threads

【讨论】:

  • 中断线程是个坏主意,很容易导致死锁。
  • 感谢您的回答。但我和 Groo 一样,知道Thread.Interrupt() 可能会导致问题。
  • 感谢@Groo 的更正,我在回答问题时也应该想到最佳做法(注意事项)。
【解决方案2】:

我可能误解了您在此处尝试实现的目标,但从我所见,您似乎正在尝试阻塞线程,直到 IO 任务完成。信号量将是最好的选择,而不是执行 Thread.Sleep()。

大多数操作系统都提供阻塞信号量,使线程永久休眠,直到另一个线程唤醒它。您最好使用它们,而不是不断地自己进行检查。

Thread.Sleep() 和阻塞信号量都会使线程进入休眠状态,但后者会一直这样做,直到资源被释放(信号量已被签名)。前者要求线程不断地唤醒、检查并重新进入睡眠状态。后者节省了这些执行周期。

【讨论】:

  • 抱歉不清楚。在 I/O 操作完成之前,我不会尝试阻塞,而是尝试暂停 I/O 操作。我基本上将数据缓冲区写入文件,直到所有数据都被写入,因为这可能需要一些时间我希望能够暂停这项工作。
  • 很好的答案!简短易懂的解释!
  • 我仍然非常喜欢你的回答,但我将 dbasnett 的回答标记为已接受,因为它更适合我的使用。 ManualResetEvent 只允许/阻止一次调用,而在使用 Semaphore 时,您必须指定 多少 它应该允许直到它被阻止。
【解决方案3】:

在 .NET 中,除了在 MTA 线程上测试和/或调试时尝试模拟冗长的操作之外,没有理由使用 Thread.Sleep,因为它会阻塞。

也许另一种选择是使用TPL。由于您不想阻止您可以使用Task.Delay。您可能知道,Task 代表一个异步操作。

【讨论】:

    【解决方案4】:

    我认为,根据描述,我会这样做

    'Class level.
    Private BytesWritten As Long = 0
    Private NotPaused As New Threading.ManualResetEvent(True)
    

    变量名的改变很合适,因为这就是它的使用方式

        'Method (thread) level.
         While BytesWritten < [target file size]
            '...write 4096 byte buffer to file...
    
            NotPaused.WaitOne(-1)
    
            '...do some more stuff...
        End While
    

    要使循环暂停,请执行此操作

        NotPaused.Reset()
    

    然后继续

        NotPaused.Set()
    

    【讨论】:

    • 谢谢。此解决方案更适合,因为 ManualResetEvent 只是“打开”或“关闭”,除了似乎为 x 次调用打开的 Semaphore。现在你还教了我一点ManualResetEvents 的工作原理!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多