2012-01-08 22 views
1

我花了一些时间寻找对此的答案,并在其他线程中找到大量有用的信息。我相信我已经以有效的方式编写了代码,但我对结果并不满意。C#使用AutoResetEvent等待来自另一线程的输入

我设计了一个硬件,我正在通过C#进行通信。硬件通过USB进行连接,并在与OS进行枚举后运行初始化程序。此时,它只是等待C#程序开始发送命令。在我的C#代码中,用户必须按下“连接”按钮,该按钮发送一个命令和所需的有效负载,让硬件知道它应该继续运行。然后硬件发送一个命令作为ACK。问题是我的C#程序必须等待接收ACK,但是GUI完全冻结,直到硬件响应,因为我不知道如何将它分区到另一个可以自由阻塞的线程。如果硬件立即响应,那么它工作正常,但如果它无法连接,那么程序将无限期冻结。

就这样说,我知道一些事情需要发生,但我不知道如何实现它们。首先,我不认为坐在等待布尔值的循环中是正确的选择,但是使用AutoResetEvent并不是真的好多了。涉及定时器,更多线程或类似的东西必须有更好的方式。

我使用与通过一个串口对象DataReceived事件检索如下:

//Send the command to signal a connection 
Send_Connection_Packet((byte)Commands.USB_UART_CMD_PC_CONNECT); 

textBox1.AppendText("-I- Attempting to contact hardware..."); 

MCU_Connect_Received.WaitOne(); 

textBox1.AppendText("Success!" + Environment.NewLine); 

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) 
{ 
    byte cmd = (byte)serialPort1.ReadByte(); 

    if (cmd == (byte)Commands.USB_UART_CMD_MCU_CONNECT) 
     MCU_Connect_Received.Set(); 

} 

在buttonClick功能(“主”线程),而它等待ACK的程序停止

理想情况下,我想知道是否超时过期,因此我可以打印“失败!”而不是“成功!”。没有超时也意味着它会永远坐在那里,正如我上面提到的,直到我杀死这个过程。有可能它不会找到任何硬件,但如果是这样,它应该在1秒钟内响应,所以2秒的超时将足够。我尝试使用Thread.Sleep,但也冻结了GUI。

回答

3

我建议您使用Task类。操作完成后,您可以使用TaskCompletionSource完成任务。

使用新async support,你的代码就变成了:

textBox1.AppendText("-I- Attempting to contact hardware..."); 
await Send_Connection_Packet((byte)Commands.USB_UART_CMD_PC_CONNECT); 
textBox1.AppendText("Success!" + Environment.NewLine); 

如果你不想使用异步CTP,那么您可以致电Task.ContinueWith并传递TaskScheduler.FromCurrentSynchronizationContext调度textBox1.AppendText("Success!")线路上运行UI线程。

异步支持还包括定时器(TaskEx.Delay)和组合子(TaskEx.WhenAny),所以你可以很容易地检查超时:

textBox1.AppendText("-I- Attempting to contact hardware..."); 
var commTask = Send_Connection_Packet((byte)Commands.USB_UART_CMD_PC_CONNECT); 
var timeoutTask = TaskEx.Delay(1000); 
var completedTask = TaskEx.WhenAny(commTask, timeoutTask); 
if (completedTask == commTask) 
    textBox1.AppendText("Success!" + Environment.NewLine); 
else 
    textBox1.AppendText("Timeout :(" + Environment.NewLine); 
+0

这是一些非常酷的东西。代码的唯一问题就是Send_Connection_Packet不处理来自硬件的返回命令,但我可以使用您的示例来实现该概念。谢谢! – Anthony 2012-01-08 04:43:08

1

GUI冻结问题是因为GUI事件的所有回调发生在运行GUI的线程中。如果你不想让GUI冻结,你需要spawn a new thread

要实现超时,您可以执行timed wait on an event handle然后检查返回值truefalse以确定呼叫是成功还是超时。

+0

谢谢!在学习如何产生新线程方面,我在寻找什么关键字?我相信在这里已经有很多次的回答,但是我似乎无法找到我要找的东西,可能是因为我使用了错误的流行语。 – Anthony 2012-01-08 04:07:53

+1

BackgroundWorker易于使用 – diggingforfire 2012-01-08 04:17:14

+1

@Anthony您正在寻找'[Thread.Start](http://msdn.microsoft.com/en-us/library/system.threading.thread.start.aspx)'(见链接)。 – 2012-01-08 08:23:11

2

如果您希望GUI保持响应,您应该在后台线程中运行。 A BackgroundWorker做得很好。我会坚持在繁忙的等待建设中进行重新调整。您可以使用定时器在超时后触发resetevent

+0

我跟着教程,它似乎是有道理的。谢谢! – Anthony 2012-01-08 04:42:10

1

要启用超时使用了WaitOne()的另一个重载:

bool succeeded = MCU_Connect_Received.WaitOne(timeOutInMilliseconds, false); 
if (succeeded) 
{ 
     textBox1.AppendText("Success!" + Environment.NewLine);   
} 
else 
{ 
     textBox1.AppendText("Failed!" + Environment.NewLine);   
} 

考虑将通信相关代码移入单独的类中以封装通信协议。这样代码将更容易维护,您将能够实现其他人建议的所有任务/后台工作者想法。

+0

谢谢!这解决了我的超时问题,这将帮助我取得进展,直到我可以实现上述解决方案之一以防止GUI冻结。 – Anthony 2012-01-08 04:41:16