【问题标题】:Why does Thread.Sleep(0) fix my problems, and how to avoid it?为什么 Thread.Sleep(0) 可以解决我的问题,以及如何避免它?
【发布时间】:2009-02-19 02:55:19
【问题描述】:

我正在开发一个客户端服务器应用程序。在某些时候,在某些机器上,当有超过 5 个客户端请求数据时,它似乎会达到死锁。如果我介入调试问题,那么程序似乎正在处理中。只需在我知道程序正在执行的地方设置一个断点,并使其多次到达断点即可完成。如果我在代码中的某些点插入 Thread.Sleep(0),主要是围绕一些 cpu 密集型循环,它似乎几乎可以完全解决问题。我现在遇到的一个问题是,如果我过多地调用 Thread.Sleep(0),它会减慢代码的速度。如果我调用它不够,代码似乎陷入僵局。虽然我可以验证它没有陷入死锁,因为如果我进入代码,它会导致问题消失,仅仅是因为我正在暂停一个线程。

有没有一种好方法可以准确追踪造成这种情况的原因。似乎它只发生在运行 Vista 的笔记本电脑上,而不发生在运行 Windows XP 的桌面上。但是,调试是不可能的,因为仅仅单步执行代码就会导致问题消失。我读过 cmets 说调用 Thread.Sleep(0) 是一种不好的做法,而且不应该是必要的,而且我不喜欢将巫术类型代码放入我不明白为什么必须存在的应用程序中.任何指针将不胜感激。

[编辑] 我还可以验证代码在“死锁”时是否仍在运行,因为如果我将其放置足够长的时间,它就会完成,只是它所花费的时间要高出许多数量级。我的意思是,当它处于这种“死锁”模式时,它实际上至少慢了 100 倍。 CPU 固定在 80-95%,所以它正在工作,尽管它正在做的事情超出了我的范围,因为完成任务需要很长时间。

[更多信息] 只是因为这里的每个人都坚持认为这是一个死锁,所以我删除了所有执行任何锁定的代码。只有几行代码可以进行任何锁定。线程在大多数情况下完全独立工作,因此完全移除锁定并没有太多工作。问题仍然存在。我的代码中不再有同步锁,不再有互斥锁,我看到的任何东西都不会导致死锁,但问题仍然存在。而且它没有陷入僵局。它运行,尽管速度很慢,尽管它占用了所有的处理器资源。

【问题讨论】:

  • 你能把代码贴出来吗?
  • 抱歉,要发布很多代码,我似乎无法用一点代码重新创建。
  • 出现问题的机器是多 CPU 的机器还是没有单 CPU 的机器?这可能会暴露许多多线程资源争用问题。
  • 不,两者都是单 CPU 机器。出现“死锁”的机器是 Celeron M 1.7 GHz,没有任何问题的机器是 Athlon 3200。
  • 如果性能在一段时间内自行解决,这不是死锁。死锁是指两个(或更多)线程各自持有另一个需要的锁,因此两者都无法继续执行其代码(直到不同的线程杀死一个或多个死锁线程)。

标签: .net multithreading


【解决方案1】:

Thread.Sleep(0) 是一个收益。我猜这会重新安排你打电话的方式以避免一些问题。我认为如果你发布带有 yield 的代码并在 1000 台机器上运行它,你会收到很多错误报告。我猜您需要某种类型的锁/关键部分来避免死锁,因为您的代码在某些地方不是线程安全的。这可能在您正在调用的库中。

  1. 添加日志,看看问题是否仍然存在。希望你能找出导致死锁的函数
  2. 添加一些关键部分。使用分而治之的方法,您应该能够缩小问题发生的范围。

【讨论】:

  • 换句话说,Sleep(0) 可能正在掩盖竞争条件。
【解决方案2】:

如果不查看整个代码库,这是不可能回答的。

调用 Thread.Sleep 是一种可怕的做法,你不应该这样做。您基本上是在改变导致死锁的代码时间,而不是从一开始就实际解决死锁条件。

调试没有显示问题的原因是,当调试器停止时,代码中的所有线程都停止了,这也导致了程序执行时间的偏差。

你要做的是在这里插入日志代码来跟踪你的代码在不同线程上的执行路径,并据此确定死锁在哪里。

【讨论】:

    【解决方案3】:

    那个循环里面是什么?如果您将检查循环中的某些字段以同步多个线程,则可能会出现类似的问题:

    while (_field); // waiting for _field change in another thread
    

    这个解决方案运行速度很慢,调用 Thread.Sleep(0) 不是一个解决方案,但在某些情况下可能是一个 hack。如果您通过调用某个同步对象(例如ManualResetEvent)的WaitHandle.WaitOne() 方法更改同步循环并在另一个线程中将此句柄放置一个信号,则可以正确修复此问题。也许你的问题是这样的?请提供您的部分代码。

    【讨论】:

      【解决方案4】:

      当它陷入困境时,有多少并发线程正在运行? 如果线程太多,CPU 可能会花费所有时间进行上下文切换。此外,每个线程会占用 1+ MB 的内存。

      【讨论】:

        【解决方案5】:

        尝试在每次锁定互斥锁时打印一行,并在解锁时打印另一行。请记住在打印时包含函数名称和线程 ID。应该让您了解它何时锁定。似乎调用 sleep(0) 的竞争条件仍会导致 CPU 使用循环处理对函数的调用。因此导致固有的睡眠。

        【讨论】:

          【解决方案6】:

          我认为是时候离开电脑,走近白板了。仔细分析每个元素是如何锁定的,以及它在什么条件下释放锁定。五个线程可能是一个难题,所以也许看看是否只有两个线程会导致相同的情况。某些东西没有正确锁定,您需要找到位置。

          除非您的代码不值得付出这么多努力,否则请将 Thread.sleep() 留在其中,因为从整体上看,它不会对您的性能造成太大影响。

          【讨论】:

            【解决方案7】:

            您肯定有需要解决的线程问题。调用 thread.Sleep(0) 会导致调度程序启动。这可能使每个线程都有机会运行足够的时间以使事情正常进行。我不会把睡眠留在那儿,因为这些东西会工作一段时间,然后完全不相关的变化最终会破坏事情。

            【讨论】:

              【解决方案8】:

              除非硬件强迫你这样做,否则你不应该使用 sleep()。

              尝试以不同的方式思考问题。考虑需要在线程之间共享哪些数据,并考虑将数据(IE,复制它)发送给相关方而不是共享访问权限的方法。如果你做对了,你实际上可能不需要任何互斥锁......

              请记住,局部变量存在于不同的堆栈中,但函数中的静态变量本质上是全局变量(当然,您需要仔细查看全局变量)。

              【讨论】:

              • 你不应该使用睡眠吗? Sleep(0) 对于诸如游戏循环之类的事情很有用,您希望尽可能多地使用处理器,而不会阻塞其他系统进程。
              【解决方案9】:

              也许尝试使用Typemock Racer之类的工具?

              免责声明:我以前从未使用过该工具。

              【讨论】:

                【解决方案10】:

                是的,您可能遇到了调度程序过载。

                我真心希望不要。

                【讨论】:

                  猜你喜欢
                  • 2011-10-08
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-01-20
                  • 2012-05-20
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-06-13
                  相关资源
                  最近更新 更多