Threadpool в C # слишком медленный, есть ли способ ускорить его? Проблемы с Thread.Sleep (0) и QueueUserWorkItem - PullRequest
1 голос
/ 10 декабря 2011

Я использую Threadpool в приложении на C #, которое должно выполнять некоторую нагрузку на процессор.Кстати, он кажется слишком медленным (РЕДАКТИРОВАТЬ: он выводит отладочную строку "Calculating on " + lSubArea.X + ":" + lSubArea.Y + " " + lSubArea.Width + ":" + lSubArea.Height только несколько раз каждые 10 секунд, хотя я ожидаю увидеть, что по крайней мере 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 - всего лишь заглушка, которую я использовал, чтобы знать, сколько раз она вызывается в секунду.Он будет заменен на интенсивную работу процессора, так как в этом случае я определю лучший способ использования потока.

РЕДАКТИРОВАТЬ: я удалил все 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 ]

3 голосов
/ 10 декабря 2011

На самом деле нет хорошего способа сказать вам, что именно делает ваш код медленным, по сравнению с тем, что я вижу, но есть несколько вещей, которые выделяются:

  1. Thread.Sleep (0).Когда вы делаете это, вы отказываетесь от остальной части своего временного интервала от ОС и замедляете все, потому что CreateDiffFrame () не может фактически вернуться, пока планировщик ОС не вернется к нему.

  2. Объект приведен к Rectangle, который является структурой.Когда это произойдет, вам придется потратить на бокс , что не будет тем, что вам нужно для действительно ресурсоемких операций.

  3. Ваши звонкиблокировка (this._state.AreaSync).Возможно, что AreaSync заблокирован где-то еще, и это может замедлить процесс.

  4. Возможно, вы ставите элементы в очередь слишком детально - если вы ставите в очередь очень маленькие элементы работывполне вероятно, что накладные расходы, связанные с размещением этих элементов в очереди по одному, могут быть значительными по сравнению с фактическим объемом выполненной работы.Возможно, вы также могли бы подумать о том, чтобы поместить содержимое внутреннего цикла в рабочий элемент в очереди, чтобы сократить эти накладные расходы.

Если это то, что вы пытаетесь сделать для параллельных вычислений,вы исследовали с помощью PLINQ или другого такого фреймворка?

0 голосов
/ 10 декабря 2011

Известна ошибка с методом ThreadPool.SetMinThreads, описанным в KB976898 :

После использования метода ThreadPool.SetMinThreads в Microsoft .NET Framework 3.5 потоки, поддерживаемые пулом потоков, не работают должным образом

Вы можете скачать исправление для этого поведения с здесь .

0 голосов
/ 10 декабря 2011

Полагаю, что это сон в конце CreateDiffFrame. Это означает, что каждый поток остается в живых по крайней мере еще 10 мс, если я правильно помню. Вы, вероятно, сможете выполнить реальную работу менее чем за 10 мс. ThreadPool пытается оптимизировать использование потоков, но я думаю, что он имеет верхний предел общего числа ожидающих потоков. Поэтому, если вы действительно хотите имитировать свою рабочую нагрузку, сделайте плотный цикл, который ждет, пока не пройдет ожидаемое количество миллисекунд, а не Sleep.

В любом случае, я не думаю, что использование ThreadPool является узким местом, использование другого механизма потоков не ускорит ваш код.

...