2012-01-08 67 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