1

これに対する答えを探すのに時間を費やしたところ、他のスレッドで役立つ情報がたくさん見つかりました。うまくいくようにコードを書いたと思いますが、結果には満足していません。

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 関数 (「メイン」スレッド) では、プログラムは 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 もフリーズしました。

4

4 に答える 4

4

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て渡して、UI スレッドで実行するように行をTaskScheduler.FromCurrentSynchronizationContextスケジュールできます。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);
于 2012-01-08T04:06:37.310 に答える
2

タイムアウトを有効にするには、WaitOne() の別のオーバーロードを使用します。

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

通信関連のコードを別のクラスに移動して、通信プロトコルをカプセル化することを検討してください。このようにして、コードの保守が容易になり、他の人が提案したすべてのタスク/バックグラウンド ワーカーのアイデアを実装できるようになります。

于 2012-01-08T04:35:19.517 に答える
2

GUI がフリーズする問題は、GUI イベントのすべてのコールバックが GUI を実行しているスレッドで発生するためです。GUI をフリーズさせたくない場合は、新しいスレッドを生成する必要があります。

タイムアウトを実装するには、イベント ハンドルで一定時間待機してtrueから、またはの戻り値をチェックしてfalse、呼び出しが成功したかタイムアウトになったかを判断します。

于 2012-01-08T04:05:13.537 に答える
1

GUI の応答性を維持したい場合は、バックグラウンド スレッドで実行する必要があります。BackgroundWorkerはこれをうまく行います。ビジーな待機構造では、resetevent に固執します。タイマーを使用して、タイムアウト期間後にリセットイベントをトリガーできます

于 2012-01-08T04:06:21.407 に答える