使用BackgroundWorker在C#中使用并发线程

时间:2010-05-11 17:09:52

标签: c# multithreading backgroundworker concurrent-processing

我的C#应用​​程序正在使用后台工作程序等待某些传输数据的确认。这是一些伪造的代码,展示了我正在尝试做的事情:

UI_thread
{
   TransmitData()
   {
      // load data for tx
      // fire off TX background worker
   }

   RxSerialData()
   {
      // if received data is ack, set ack received flag
   }
}

TX_thread
{
   // transmit data
   // set ack wait timeout
   // fire off ACK background worker
   // wait for ACK background worker to complete
   // evaluate status of ACK background worker as completed, failed, etc.
}

ACK_thread
{
   // wait for ack received flag to be set
}

ACK BackgroundWorker会超时,并且永远不会收到确认。我很确定它是由远程设备传输的,因为该设备根本没有改变,而且C#应用程序正在传输。我已经改变了ack线程(当它工作时)......

for( i = 0; (i < waitTimeoutVar) && (!bAckRxd); i++ )
{
   System.Threading.Thread.Sleep(1);
}

......对此...

DateTime dtThen = DateTime.Now();
DateTime dtNow;
TimeSpan stTime;

do
{
   dtNow = DateTime.Now();
   stTime = dtNow - dtThen;
}
while ( (stTime.TotalMilliseconds < waitTimeoutVar) && (!bAckRxd) );

与前者相比,后者产生非常精确的等待时间。但是,我想知道删除睡眠功能是否会干扰接收串行数据的能力。 C#是否只允许一次运行一个线程,也就是说,我是否必须在一段时间内让线程进入休眠状态以允许其他线程运行?

您可能会有任何想法或建议,我们将不胜感激。我使用的是Microsoft Visual C#2008 Express Edition。感谢。

3 个答案:

答案 0 :(得分:3)

为了回答C#是否允许一次运行一个线程的直接问题,C#与线程没有任何关系。但是,.NET框架允许您一次运行多个(逻辑)线程。如何实际处理是框架和操作系统的功能。

至于你的问题,我认为你有太多的线程处于等待状态开始。您发送和接收数据的方法应该具有异步调用模型(开始和结束)。因此,您应该通过调用Begin开始传输,然后附加在函数终止时调用的回调。

然后,在回调中,您将处理结果并继续执行下一个异步操作(如有必要)或更新UI。

答案 1 :(得分:3)

你的“新”版本的RX线程正在利用100%的处理器时间 - 它只是连续运行而且从不睡觉。除了这种方法的一般邪恶性质,这个可能(虽然不一定)阻止其他一些事情,如接收数据,按时发生。

对于这样的等待场景,通常会使用名为 event 的线程同步构造。您创建一个“事件”对象,并且RX线程等待它,而处理线程在收到ACK时发出信号

AutoResetEvent event = new AutoResetEvent( false );

// ...
// ACK waiting thread:
event.WaitOne();
// ...

// ...
// Whatever thread actually receives the ACK
if ( /* ack received */ )
{
    // bAckRxd = true; - comment this out. Replace with following:
    event.Set();
}

至于为什么你没有收到你的ACK,我需要更多的信息。通道究竟是什么?它是串口吗?网络?管?还有别的吗?

答案 2 :(得分:1)

您的代码非常高兴。当你期望他们计时时,这确实会让你陷入困境。特别是当您使用BGW或线程池线程时,调度程序仅允许它们在没有比CPU内核更多的线程活动时运行。或者当线程被“卡住”一段时间。你的卡住了。您似乎也没有有效地使用它们,轮询循环会消耗大量不必要的CPU周期。

利用SerialPort类的功能来避免这种情况:

  • 您不需要传输线程。串口驱动程序有一个缓冲区,当数据适合缓冲区时,Write()调用将立即返回。从主线程写作很好。
  • 您不一定需要接收线程。串口已经有一个,它运行DataReceived事件。它可以阻止您在传输数据时启动的Timer。
  • SerialPort已具有ReadTimeout属性。您可以在接收线程中使用它来超时Read()调用。

Sleep()不会干扰串口,它们的驱动程序使用硬件中断读取数据。