【问题标题】:Speed data processing: ThreadPool or Thread loop with event?加速数据处理:线程池或线程循环与事件?
【发布时间】:2014-06-11 06:44:23
【问题描述】:

我需要在另一个线程中处理数据。可以通过两种方式完成:

  1. 使用线程循环等待事件:

    AutoResetEvent e = new AutoResetEvent(false)
    Thread t = new Thread(delegate
    {
      while(true)
      {
        e.WaitOne();
        // process data
      }
    };)
    
    void OnProgramStarted() // one time
    {
      t.Start();
    }
    
    void OnDataReceived()
    {
      // put data to queue
      e.Set();
    }
    
  2. 使用线程池:

    void ProcessData(object state)
    {
      // process data
    }
    
    void OnDataReceived()
    {
      // put data to queue
      ThreadPool.QueueUserWorkItem(ProcessData);
    }
    

什么方法会更快?
真实的测试给出了模棱两可的结果。

我的基准测试代码:

using System;
using System.Diagnostics;
using System.Threading;

namespace t_event_tpool
{
  class Program
  {
    const int t = 1000000;

    static Stopwatch sw = new Stopwatch();
    static int q1, q2;


    static AutoResetEvent e1 = new AutoResetEvent(false);
    static AutoResetEvent done1 = new AutoResetEvent(false);

    static Thread thread = new Thread(ThreadProc);
    static void ThreadProc(object state)
    {
      while(true)
      {
        e1.WaitOne();
        q1++;
        done1.Set();
      }
    }


    static AutoResetEvent done2 = new AutoResetEvent(false);

    static void PoolProc(object state)
    {
      q2++;
      done2.Set();
    }


    static void TestA()
    {
      sw.Restart();
      for(int i = 0; i < t; i++)
      {
        e1.Set();
        done1.WaitOne();
      }
      sw.Stop();
      Console.WriteLine("a " + sw.ElapsedMilliseconds + "\t" + q1);
    }

    static void TestB()
    {
      sw.Restart();
      for(int i = 0; i < t; i++)
      {
        ThreadPool.QueueUserWorkItem(PoolProc, i);
        done2.WaitOne();
      }
      sw.Stop();
      Console.WriteLine("b " + sw.ElapsedMilliseconds + "\t" + q2);
    }



    static void Main(string[] args)
    {
      thread.IsBackground = true;
      thread.Start();

      TestA();
      TestB();

      TestA();
      TestB();

      TestA();
      TestB();
    }
  }
}

在低 CPU 负载下(没有其他应用程序)TestB 比 TestA 快 2 倍。在其他进程的 CPU 负载较高时,TestA 比 TestB 更快。

【问题讨论】:

  • 很可能是线程池方法,因为线程池旨在加快线程的初始化时间。
  • 我有固定的代码。当然,线程只在程序启动时初始化一次。
  • 看起来BlockingCollection&lt;T&gt; 是一个选项:一个线程(主线程?)向其中添加数据,另一个线程获取并处理数据。
  • QUWI 相当快,但不如可以立即继续运行的线程快。如果您得到“模棱两可的结果”,那么请不要打扰并偏爱 QUWI。
  • 我认为线程也应该更快。但测试表明,在低 CPU 负载下(没有其他应用程序)QUWI 速度快 1.5-2 倍。如果 CPU 负载很高(其他应用程序),则测试结果相反。我不明白为什么会这样。也许我的测试不正确。

标签: c# multithreading threadpool


【解决方案1】:

您正在测试开始工作的开销。这对于实际工作的速度没有意义。与其优化开销,不如让你的工作项更细化,这样开销就无关紧要了。如果您的工作项目运行速度超过 0.1 毫秒,我会认真考虑让它们更大。在您的测试中,您的工作项只需要几个指令。

回答这个问题:与将工作项发布到线程池相比,准备好在事件信号下运行的自定义线程的开销更少。你几乎什么都没做。很难改进。您需要使用高速排队模式,例如 Disruptor 模式。

【讨论】:

  • 我需要尽量减少间接费用,问题就是这个。
  • 在这种情况下,看看是否有 .NET 的 Disruptor 模式的实现。请注意,在负载许多工作项的情况下,线程池的开销可能会更少,因为它只需要从其工作队列中拉出而不使用操作系统级别的同步。它在负载下变得更加高效。 所以执行一个现实的基准测试。我不相信你想使用线程来增加整数。
  • 增量尽可能接近现实 :) 我需要尽量减少事件和线程反应之间的延迟。活动不经常。
  • 那么使用多线程将一无所获。通信成本至少比整数增量高 2 个数量级。
  • 此测试中的增量只是为了禁用可能的优化。不影响结果。
猜你喜欢
  • 1970-01-01
  • 2019-11-09
  • 2010-09-18
  • 2013-01-11
  • 1970-01-01
  • 2011-11-03
  • 2015-03-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多