.Net Thread vs ThreadPool vs SerialPort Communication的任务

时间:2012-04-12 03:54:33

标签: .net multithreading threadpool task

我在使用SerialPort类以及ThreadPool.QueueUserWorkItemTask的C#.Net 4.0应用程序中遇到了一个有趣的问题。

如果我同时使用2个或更多SerialPort,则只会出现此问题。每个串口都在自己的线程中运行,我以3种方式之一创建:

  1. new Thread(DoSerialCommX)
  2. ThreadPool.QueueUserWorkItem(DoSerialCommX)
  3. new Task(DoSerialCommX, TaskCreationOptions.LongRunning).Start()
  4. 为了说明这个问题,我创建了DoSerialCommX方法,以便在循环中永久地读取和写入串行端口。它看起来像这样:(我实际上并没有在我的真实程序中执行此操作。这只是我的测试程序中的一个片段,它隔离并说明了问题)。

    private void DoSerialCommX()
    {
        SerialPort port = new SerialPort("ComX", 9600);
        port.Open();
    
        while(true)
        {
            //Read and write to serial port
        }
    }
    

    如果我使用方法2或3,则串行通信会断断续续,并且会收到许多通信超时。如果我使用方法1,一切都很好。此外,我应该提到这似乎只发生在我的基于英特尔凌动的PC上。台式电脑似乎没有任何问题。

    我知道线程池重用了线程,默认情况下Task使用线程池。我知道线程池真正用于短期操作。但我尝试使用TaskCreationOptions.LongRunning,我认为它会产生一个专用线程,而不是使用线程池,但它仍然无效。

    所以问题:在这种情况下Thread如此特别的原因是什么?是否有关于Thread的内容使其更适合IO操作?

    修改 到目前为止,答案似乎假设我正在尝试使用ThreadPool或任务进行永无止境的过程。在我的实际应用中,情况并非如此。我只是在上面的代码中使用了一个永无止境的循环来说明问题。我真的需要知道为什么Thread有效,ThreadPoolTask没有。什么在技术上有所不同会导致串行通信打嗝?

4 个答案:

答案 0 :(得分:2)

哲学上,执行线程的行为方式在1,2和3之间几乎没有区别。它们都共享相同的默认执行优先级,除非您覆盖它 - 从概念上讲,线程调度程序将使用相同的策略来安排它们。他们都快乐地坐在旋转中。

我怀疑方法之间的差异更大:

  1. 基础架构成本(线程,内存等)分支支持#2和#3的线程池,所有这些都将与执行循环争用时钟时间。
  2. 线程上下文切换成本较低的肌肉Atom。 Atom有一个较小的缓存, 和更短的处理管道。更多正在运行的线程=更多的上下文切换,更多的管道转储和更低的指令缓存效率。
  3. 从功能的角度来看,方法2和3的使用有点滥用 - 你的意图是永远不要退出方法。这些策略针对原子,有限操作以及适用于IO完成端口任务(如异步网络,磁盘操作等)的某些非制导执行进行了优化(也可能是您的串行端口代码的可能性?)

    我会跳过2& 3,除非您有兴趣适应Async IO。专注于线程 - 似乎你想要更精细的粒度,可预测的执行控制,而没有Threadpool带来的基础架构开销。

答案 1 :(得分:1)

我怀疑发生这种情况,因为您正在从COM端口读取/写入。如果没有这个因素,所有这些都应该完全相同,因为(如果你检查)它们都在普通优先级的线程中运行。

也许请阅读I/O completion ports和线程池,看看是否可以解释这种奇怪的行为。

答案 2 :(得分:1)

您的代码,驱动程序或硬件都会使您的串口性能“处于边缘”,以至于它一直处于不工作状态。使用专用线程或线程池应该没有问题,(mod。使用线程池线程阻塞串口读取)。

两个9600波特串口,你可以用算盘运行,提供珠子和电线都很好。

答案 3 :(得分:0)

存在轻微差异 - 导致超时:

  • 当Thread.Start()为时,使用Thread显式创建一个新线程 调用 - 它确保您的代码在特定时刻运行。
  • 使用ThreadPool可确保在近处打开一个线程 未来根据内部的ThreadPool逻辑。因为ThreadPool 数量是有限的,建议长时间(或无限)使用它 正在运行。
  • 使用任务只会确定代码会运行。不管是在 另一个线程甚至在主线程上并没有确保它 将立即运行或不同的任务将运行在不同的 线程。

因此,如果您希望您的任务快速启动,请始终使用Thread。任务和ThreadPool都有一些额外的基础设施开销"这可能会导致你注意到的轻微延迟。

由于当您的任务从未存在时,并不打算使用ThreadPool和任务,我建议您使用Thread。