线程池在c#中太慢了,有没有办法加速它?Thread.Sleep(0)和QueueUserWorkItem问题

本文关键字:Sleep Thread 问题 QueueUserWorkItem 加速 有没有 线程 | 更新日期: 2023-09-27 18:17:39

我在一个需要做一些cpu密集型工作的c#应用程序中使用Threadpool。顺便说一下,它似乎太慢了(编辑:它每10秒只打印出调试字符串"Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height几次,而我期望看到至少NUM_ROWS_GRID^2 = 16次每隔几秒),也通过SetMinThreads方法改变MinThreads。我不知道是否切换到自定义线程,或者是否有办法加快它。在谷歌搜索返回一些结果,但没有工作;MSDN也是如此。

旧代码如下:

private void StreamerRoutine()
{
   if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
      this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
   while (this._state.WorkEnd == false)
   {
      // Ends time slice if video is off
      if (this._state.VideoOn == false)
         Thread.Sleep(0);
      else
      {
         lock(this._state.AreaSync)
         {
             Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
             Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
             for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
                for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
                   ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));
         }
      }
    }
}
private void CreateDiffFrame(Object pState)
{
   Rectangle lSubArea = (Rectangle)pState;
   SmartDebug.DWL("Calculating on " 
          + lSubArea.X + ":" + lSubArea.Y + " " 
          + lSubArea.Width + ":" + lSubArea.Height);
   // TODO : calculate frame
   Thread.Sleep(0);
}

编辑:CreateDiffFrame函数只是一个存根,我用来知道每秒调用多少次。它将被替换为CPU密集型工作,因为我定义了在这种情况下使用线程的最佳方式。

编辑:我删除了所有的Thread.Sleep(0);我认为这可能是一种加快日常工作的方法,但似乎它可能成为瓶颈。新代码如下:

编辑:我使WorkEnd和VideoOn易失性,以避免缓存值和无限循环;我还添加了一个信号量,使每一堆工作项在前一堆完成后开始。现在它工作得很好

private void StreamerRoutine()
    {
        if (this._state.Area.Width == 0 && this._state.Area.Height == 0)
            this._state.Area = new Rectangle(0, 0, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
        this._state.StreamingSem = new Semaphore(Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID, Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID);

        while (this._state.WorkEnd == false)
        {
            if (this._state.VideoOn == true)
            {
                for (int i = 0; i < Constants.NUM_ROWS_GRID * Constants.NUM_ROWS_GRID; i++)
                    this._state.StreamingSem.WaitOne();
                lock(this._state.AreaSync)
                {
                    Int32 lWidth = this._state.Area.Width / Constants.NUM_ROWS_GRID;
                    Int32 lHeight = this._state.Area.Height / Constants.NUM_ROWS_GRID;
                    for (Int32 lX = 0; lX + lWidth <= this._state.Area.Width; lX += lWidth)
                        for (Int32 lY = 0; lY + lHeight <= this._state.Area.Height; lY += lHeight)
                            ThreadPool.QueueUserWorkItem(CreateDiffFrame, (Object)new Rectangle(lX, lY, lWidth, lHeight));
                }
            }
        }
    }
private void CreateDiffFrame(Object pState)
    {
        Rectangle lSubArea = (Rectangle)pState;
        SmartDebug.DWL("Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height);
        // TODO : calculate frame
        this._state.StreamingSem.Release(1);
    }

线程池在c#中太慢了,有没有办法加速它?Thread.Sleep(0)和QueueUserWorkItem问题

从我所看到的情况来看,确实没有一个好的方法来告诉您究竟是什么使您的代码变慢,但是有一些事情很突出:

  1. thread . sleep(0)。当你这样做的时候,你放弃了OS的剩余时间片,并减慢了一切,因为CreateDiffFrame()在OS调度器返回之前实际上不能返回。

  2. Rectangle的对象强制转换,它是一个结构体。当这种情况发生时,您会产生装箱的开销,这不是您想要的真正的计算密集型操作。

  3. 你的锁调用(this._state.AreaSync)。这可能是AreaSync也被锁定在其他地方,这可能会减慢速度。

  4. 您可能将项排队的粒度太细了——如果您将非常小的工作项排队,那么与实际完成的工作量相比,每次将这些项放入队列的开销可能会很大。您还可以考虑将内部循环的内容放在排队的工作项中,以减少此开销。

如果这是你试图为并行计算做的事情,你是否研究过使用PLINQ或其他类似的框架?

我猜这是在CreateDiffFrame结束时的Sleep。如果我没记错的话,这意味着每个线程至少还能存活10毫秒。您可以在不到10毫秒的时间内完成实际的工作。ThreadPool试图优化线程的使用,但我认为它有一个未完成线程总数的上限。因此,如果您想实际模拟您的工作负载,请创建一个紧循环,直到预期的毫秒数过去,而不是Sleep。

无论如何,我不认为使用ThreadPool是真正的瓶颈,使用其他线程机制不会加快你的代码。

在KB976898中描述的ThreadPool.SetMinThreads方法存在已知错误:

使用ThreadPool后。.NET Framework 3.5中的SetMinThreads方法,由线程池维护的线程不能按预期工作

你可以从这里下载修复这个行为