BackgroundWorker を使用した C# の同時スレッド
-
26-09-2019 - |
質問
私の 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# では一度に 1 つのスレッドのみを実行できますか? つまり、他のスレッドを実行できるようにするには、ある時点でスレッドをスリープ状態にする必要がありますか?
ご意見やご提案をいただければ幸いです。Microsoft Visual C# 2008 Express Edition を使用しています。ありがとう。
解決
RX スレッドの「新しい」バージョンは、プロセッサ時間を 100% 利用しています。継続的に実行され、スリープすることはありません。このアプローチの一般的な邪悪な性質に加えて、 かもしれない (必ずではありませんが) データの受信など、他の処理が予定通りに行われるのを妨げます。
このような待機シナリオの場合、通常は、と呼ばれるスレッド同期構造を利用します。 イベント. 。「イベント」オブジェクトを作成すると、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 を受信できない理由については、さらに詳しい情報が必要です。チャンネルとは具体的に何ですか?シリアルポートですか?通信網?パイプ?他に何か?
他のヒント
C#は1つのスレッドが一度に実行することを可能にするかどうかの直接的な質問に答えるために、C#はスレッドとは何の関係もありません。 NETフレームワークは、しかし、あなたは一度に複数の(論理的)スレッドを実行できるようになります。フレームワークとOSの機能がどのようにそれが実際に処理されている。
あなたの問題については、私はあなたがそもそも待機状態であまりにも多くのスレッドを持っていると思います。データを送受信するあなたの方法には、それらの非同期呼び出しモデルを持っている必要があります(開始と終了)。このため、作業を開始し、その後、関数終了に呼び出されるコールバックを添付するための呼び出しを使用して送信を開始する必要があります。
次に、コールバックで、あなたは結果を処理し、次の非同期動作に進む(必要な場合)またはUIを更新します。
あなたのコードはむしろスレッドハッピーです。彼らに時間を計ってもらうことを期待すると、実際に問題が発生する可能性があります。特に、BGW またはスレッド プール スレッドを使用する場合、スケジューラは、CPU コアよりも多くのスレッドがアクティブになっていない場合にのみ、それらの実行を許可します。または、スレッドがしばらく「スタック」した場合。あなたは行き詰まってしまいます。また、ポーリング ループは不必要な CPU サイクルを大量に消費するため、それらを効果的に使用していないようです。
これを回避するには、SerialPort クラスの機能を活用します。
- 送信スレッドは必要ありません。シリアル ポート ドライバーにはバッファーがあり、データがバッファーに収まると、Write() 呼び出しは即座に戻ります。メインスレッドからの書き込みは問題ありません。
- 受信スレッドは必ずしも必要ではありません。シリアル ポートにはすでに 1 つがあり、DataReceived イベントが実行されます。データを送信したときに開始したタイマーが中断される可能性があります。
- SerialPort にはすでに ReadTimeout プロパティがあります。これを受信スレッドで使用すると、Read() 呼び出しをタイムアウトできます。
Sleep() はシリアル ポートに干渉せず、ドライバはハードウェア割り込みを使用してデータを読み取ります。