【问题标题】:.NET Bug - Threading.Timer affects Parallel.For performance.NET 错误 - Threading.Timer 影响 Parallel.For 性能
【发布时间】:2010-11-03 20:24:20
【问题描述】:

我有一个使用顺序 Parallel.For 语句的 VB.NET WinForms 应用程序。

添加 Threading.Timer 后,Parallel.For 循环慢到爬行,内核时间量跃升至近 80%。

这两者在我的代码中不相关(即两者都不由另一个运行),也不共享任何代码或数据。

删除计时器可以解决问题。

有没有其他人经历过这种行为?解决此行为的最佳方法是什么(宁愿不创建 WinForms 计时器,但如果必须的话)?

这是定时器执行的代码:

Public Sub Tick()
  IO.File.WriteAllText("c:\test.txt", DateTime.Now.ToString())
End Sub

这是定时器创建语句:

tmrFile = New Threading.Timer(AddressOf Tick, Nothing, New TimeSpan(0, 0, 0), New TimeSpan(0, 1, 0))

【问题讨论】:

  • 有趣..你能发布一些重现问题的简单代码吗?
  • 简单在这种情况下可能不是很简单。并行 fors 正在执行大量内存复制和图像重采样。线程计时器只是用当前日期时间更新文件。如果我有时间,我会看看我是否不能举个例子来重现这种行为。
  • @ColorEyes,如果你删除了 Parallel.For 循环,只是定时器的东西会让你的机器忙吗?
  • @Albin - 如果我注释掉 Parallel.For 循环,程序似乎运行正常(尽管没有完成任何工作)。因此,取出循环或计时器使程序运行正常。也许运行计时器和循环会以某种方式破坏 ThreadPool(假设它们都使用线程池)。
  • @ColorEyes .. 如果你保持计时器,但不做任何有用的事情?计时器多久运行一次?

标签: .net multithreading timer


【解决方案1】:

您的计时器是否经常发出信号?如果是这样,您可能会对从计时器触发的线程数感到不知所措。可以运行的线程数量是有限的,如果 Timer 因过于频繁地发出信号而花费了太多时间,那么您的其余代码将会受到影响。

你能分析你的计时器在做什么并分享这些信息吗?它多久发出一次信号,它正在执行的任何特别粗略的代码,它是否也在执行并行任务等等。

【讨论】:

  • 每分钟发出一次信号。当它发出信号时,它将当前日期和时间写入文件。我尝试将其增加到 10 分钟,但存在相同的行为。
  • 如何写入文件?锁定打开、写入、刷新、关闭操作?只是希望在下一个信号的下一个线程之前完成写入?使用计时器时代码开始变慢还是随着时间变慢?
  • 如果您对这些详细信息感兴趣,代码会在上面发布。但是,这并不重要,因为即使注释掉了代码(即空勾号),行为仍然会表现出来。
  • 查看各种文档,似乎 Timer 使用 ThreadPool 线程。 Timer 上的文档揭示了这一点,并披露即使在因此调用 Dispose 之后仍可能发生信号。所以,我想知道您的 Parallel.For 语句正在尝试使用多少个 ThreadPool 线程,以及您的 Timer 正在使用多少个线程。 ThreadPool 上有一些静态方法可以让您根据需要进行调查。我想知道你是否过度分配。线程使用信息:msdn.microsoft.com/en-us/library/…
【解决方案2】:

尝试将其从线程池中取出!我整理了一个简单的计时器类供您试用(请注意,不是生产代码)。此类在单独的线程中运行。

班级:

  public class ThreadedTimer : IDisposable
  {
     private readonly AutoResetEvent _isStopping = new AutoResetEvent(false);
     public ThreadedTimer(Action f, int interval)
     {
        Thread t = new Thread(() =>
        {
           while (!_isStopping.WaitOne(interval))
           {
              f();
           }
        });
        t.IsBackground = true;
        t.Start();
     }

     public void Dispose()
     {
        _isStopping.Set();
     }
  }

启动计时器:

var timer = new ThreadedTimer(() => Debug.WriteLine("Test"), 1000);

更新: 下面的VB中完全未经测试的代码

Imports System.Threading
Public Class ThreadedTimer
    Implements IDisposable
    Private ReadOnly _isStopping As New AutoResetEvent(False)

    Public Sub New(ByVal f As Action, ByVal interval As Integer)
        Dim t As New Thread(Sub()
                                While Not _isStopping.WaitOne(interval)
                                    f()
                                End While
                            End Sub)
        t.IsBackground = True
        t.Start()
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        _isStopping.[Set]()
    End Sub
End Class

【讨论】:

  • 谢谢,但是这段代码是如此的琐碎和如此明显的解决方案,以至于我已经实现了我自己的线程计时器。此外,我的问题不在于如何实现琐碎的解决方案,这就是为什么 Threading.Timer 不能与我的代码一起使用?这就是我要解决的问题的症结所在。
  • @ColorEyes 对不起,它没有帮助,但没有理由对此感到困惑。有点违背本站的精神。怎么会有人知道你认为“显而易见的解决方案”?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-17
  • 2015-04-08
  • 1970-01-01
相关资源
最近更新 更多