我花了一些时间寻找答案,并在其他线程中找到了大量有用的信息。我相信我已经以一种有效的方式编写代码,但我对结果并不满意。
我设计了一个通过C#与之通信的硬件。硬件通过USB连接并在使用OS枚举后运行初始化例程。此时,它只是等待C#程序开始发送命令。在我的C#代码中,用户必须按下“连接”按钮,该按钮发送命令和所需的有效负载,让硬件知道它应该继续运行。然后硬件将命令作为ACK发回。问题是我的C#程序必须等待接收ACK,但GUI完全冻结,直到硬件响应,因为我不知道如何将它分区到另一个可以自由阻塞的线程。如果硬件立即响应,那么它工作正常,但如果它无法连接,那么程序将无限期地保持冻结状态。
话虽如此,我知道有些事情需要发生,但我不确定如何实施它们。首先,我不认为坐在循环中等待布尔值是正确的方法,但使用AutoResetEvent似乎并没有好多少。必须有一种更好的方法,包括计时器,更多线程或类似的东西。
我正在使用带有serialPort对象的DataReceived事件,如下所示:
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函数(“main”线程)中,程序在等待ACK时停止:
//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);
理想情况下,我想知道超时是否到期,以便打印“失败!”而不是“成功!”。没有超时也意味着它将永远坐在那里,如上所述,直到我杀死进程。有可能它找不到任何硬件,但如果有的话,它应该以< 1秒,所以超时2秒就足够了。我尝试使用Thread.Sleep,但这也冻结了GUI。
答案 0 :(得分: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);
如果您不想使用Async CTP,那么您可以调用Task.ContinueWith
并传递TaskScheduler.FromCurrentSynchronizationContext
以安排在{i}行上运行textBox1.AppendText("Success!")
行。
异步支持还包括计时器(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);
答案 1 :(得分:2)
如果您希望GUI保持响应,您应该在后台线程中运行。 BackgroundWorker做得很好。在忙碌的等待建设中,我坚持使用重新安装。您可以使用计时器在超时期限后触发重新发送
答案 2 :(得分:1)
GUI冻结的问题是因为GUI事件的所有回调都发生在运行GUI的线程中。如果您不想冻结GUI,则需要spawn a new thread。
为了实现超时,您可以执行timed wait on an event handle,然后检查true
或false
的返回值,以确定呼叫是否成功或是否超时。
答案 3 :(得分:1)
要启用超时,请使用WaitOne()的另一个重载:
bool succeeded = MCU_Connect_Received.WaitOne(timeOutInMilliseconds, false);
if (succeeded)
{
textBox1.AppendText("Success!" + Environment.NewLine);
}
else
{
textBox1.AppendText("Failed!" + Environment.NewLine);
}
考虑在单独的类中移动与通信相关的代码以封装通信协议。这样代码将更容易维护,您将能够实现其他人建议的所有任务/后台工作者的想法。