【问题标题】:Thread type for TCP Accept loop: BackgroundWorker, Thread, or ThreadPoolTCP Accept 循环的线程类型:BackgroundWorker、Thread 或 ThreadPool
【发布时间】:2011-04-19 23:34:03
【问题描述】:

我正在编写一个 TCP 服务器,它的核心是一段相当标准的 bind-listen-accept 代码,由 TcpListener 很好地封装。我正在开发中运行的代码现在工作,但我正在寻找有关我选择的线程模型的一些讨论:

        // Set up the socket listener
        // *THIS* is running on a System.Threading.Thread, of course.
        tpcListener = new TcpListener(IPAddress.Any, myPort);
        tpcListener.Start();
        while (true)
        {
            Socket so = tpcListener.AcceptSocket();
            try
            {
                MyWorkUnit work = new MyWorkUnit(so);
                BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += new DoWorkEventHandler(DispatchWork);
                bw.RunWorkerCompleted += 
                                new RunWorkerCompletedEventHandler(SendReply);
                bw.RunWorkerAsync(work);
            }
            catch (System.Exception ex)
            {
                EventLogging.WindowsLog("Error caught: " + 
                                     ex.Message, EventLogging.EventType.Error);
            }
        }

我已经看到了关于选择哪种线程模型(BackgroundWorker、stock Thread 或 ThreadPool)的很好的描述,但没有一个适用于这种情况。 backgroundworker-vs-background-thread (第二个答案)是对每种优缺点的一个很好的总结。在上面的示例代码中,我选择了 BackgroundWorker,因为它很简单。是时候弄清楚这是否是正确的方法了。

这些是这个应用程序的细节,对于大多数类似事务的 TCP 服务器来说,它们可能是相当标准的:

  • 不是 Windows 窗体应用程序。事实上,它作为 Windows 服务运行。
  • (我不确定生成的工作是否需要成为前台线程。我现在将它们作为后台运行,一切正常。)
  • 只要Accept() 循环获得循环,分配给线程的任何优先级都可以。
  • 对于以后的Abort() 或其他什么,我不需要线程的固定 ID。
  • 在线程中运行的任务很短——最多几秒钟。
  • 可能有很多任务会很快进入这个循环。
  • 如果我没有线程,拒绝(或排队)新工作的“优雅”方式会很好。

那么,哪种方法才是正确的呢?

【问题讨论】:

  • 有人在Answer中建议了这种编码方法并删除了它。 stackoverflow.com/questions/62449/… 但也许不是。细节会很好。这是一个正常的线程,对吧?如果它死了,我的进程就死了吗?

标签: c# multithreading tcp


【解决方案1】:

对于这个主线程,使用一个单独的 Thread 对象。它运行时间很长,不太适合线程池(Bgw 使用线程池)。

在这里创建它的成本并不重要,您(可能)希望完全控制线程的属性。

编辑

对于传入的请求,您可以使用 ThreadPool(直接或通过 Bgw),但请注意这可能会影响您的吞吐量。当所有线程都忙时,在创建额外线程之前会有延迟(0.5 秒)。这个 ThreadPool 行为可能有用,也可能没用。您可以调整 MinThreads 以对其进行一些控制。

这很粗糙,但如果您要为衍生任务创建自己的线程,您可能必须想出自己的节流机制。

这完全取决于您期望的请求数量以及它们的大小。

【讨论】:

  • 是的,是的;任何需要成为单独线程并且基本上运行应用程序的整个持续时间的任务最终都应该在成熟的 Thread 实例上运行。 +1
  • 我认为你错过了重点。主线程在 System.Threading.Thread 上运行(或者甚至可能是应用程序的主线程),我将其添加到代码中作为描述。我为衍生任务使用什么样的线程?
【解决方案2】:

BackgroundWorker 似乎是您的员工的不错选择。我唯一需要注意的是确保您不会阻塞这些线程本身的网络流量。酌情使用 Async 方法在此处发送/接收,以免 ThreadPool 线程因网络流量而被阻塞。

这些线程也可以作为后台线程(非常合适)。唯一真正的区别是,在正常情况下,前台线程会保持进程处于活动状态。

另外,我认为您没有提到捕获这些连接的主线程;那个适用于常规 System.Threading.Thread 实例。

【讨论】:

  • 主线程位于常规 System.Treading.Thread 实例上。我认为这与讨论无关,因此省略了。
【解决方案3】:

嗯,就个人而言,这些都不是我的首选。我倾向于通过利用 Socket.BeginReceiveSocket.BeginSend 来更喜欢异步 IO 操作,并让底层 IO 完成端口为您完成所有线程。但是,如果您更喜欢使用同步 IO 操作,那么将它们切换到 ThreadPoolTask(如果使用 .NET 4.0)将是下一个最佳选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2019-12-01
    • 1970-01-01
    相关资源
    最近更新 更多