C#中的Threadpool太慢了,有没有办法加速呢? Thread.Sleep(0)和QueueUserWorkItem问题

时间:2011-12-10 14:49:08

标签: c# multithreading performance threadpool queueuserworkitem

我在C#应用程序中使用Threadpool,需要做一些CPU密集型工作。顺便说一句它似乎太慢了(编辑:它每10秒打印出调试字符串"Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height只有几次,而我期望每隔几秒钟至少看到NUM_ROWS_GRID ^ 2 = 16次),同时也在变化MinThreads通过SetMinThreads方法。我不知道是否切换到自定义线程或是否有办法加快它。在Google上搜索会返回一些结果,但没有任何效果;与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);

    }

3 个答案:

答案 0 :(得分:3)

确实没有一种好方法可以告诉你究竟是什么让你的代码从我看到的东西变慢,但有几件事情很突出:

  1. 的Thread.Sleep(0)。执行此操作时,您将从操作系统中放弃剩余的时间片,并减慢所有操作,因为在操作系统调度程序返回之前,CreateDiffFrame()实际上无法返回。

  2. Rectangle的对象转换,它是一个struct。当发生这种情况时,您会产生boxing的开销,这对于真正的计算密集型操作来说并不是您想要的。

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

  4. 您可能过于精细地对项目进行排队 - 如果您排队非常小的工作项,那么将这些项目一次一个地放入队列的开销可能与实际的实际数量相比可能很大。完成工作。您也可以考虑将内部循环的内容放在排队的工作项中以减少这种开销。

  5. 如果您正在尝试进行并行计算,请使用PLINQ或其他此类框架进行调查吗?

答案 1 :(得分:0)

我的猜测是,它是CreateDiffFrame结束时的Sleep。这意味着如果我没记错的话,每个线程至少会保持10毫秒。您可以在不到10毫秒的时间内完成实际工作。 ThreadPool尝试优化线程的使用,但我认为它有未完成线程总数的上限。因此,如果您想真正模仿您的工作负载,请进行一个紧密的循环,等待直到预期的毫秒数而不是Sleep。

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

答案 2 :(得分:0)

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

  

在Microsoft .NET Framework 3.5中使用ThreadPool.SetMinThreads方法后,线程池维护的线程无法按预期工作

您可以从here下载此行为的修补程序。

相关问题