0

私の 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 を使用しています。ありがとう。

4

3 に答える 3

3

C#で一度に1つのスレッドを実行できるかどうかという直接的な質問に答えるために、C#はスレッド化とは何の関係もありません。ただし、.NET Frameworkを使用すると、一度に複数の(論理)スレッドを実行できます。それが実際にどのように処理されるかは、フレームワークとOSの機能です。

問題は、待機状態のスレッドが多すぎると思います。データを送受信するメソッドには、非同期呼び出しモデル(BeginとEnd)が必要です。このため、Beginの呼び出しで送信を開始してから、関数の終了時に呼び出されるコールバックをアタッチする必要があります。

次に、コールバックで、結果を処理して次の非同期操作に進むか(必要な場合)、UIを更新します。

于 2010-05-11T17:18:22.403 に答える
3

RX スレッドの「新しい」バージョンは、プロセッサ時間を 100% 使用しています。継続的に実行され、スリープすることはありません。このアプローチの一般的な邪悪な性質に加えて、これにより、(確実ではありませんが) データの受信など、他のいくつかのことが予定どおりに行われなくなる可能性があります。

このような待機シナリオでは、通常、eventと呼ばれるスレッド同期構造を利用します。「イベント」オブジェクトを作成すると、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 を受信して​​いない理由については、さらに情報が必要です。チャンネルって具体的に何?シリアルポートですか?通信網?パイプ?他の何か?

于 2010-05-11T17:22:05.577 に答える
1

あなたのコードはかなりスレッドに適しています。彼らが時間を計ることを期待しているとき、それは確かにあなたを困らせる可能性があります. 特に、BGW またはスレッド プール スレッドを使用する場合、スケジューラは、アクティブなスレッドが CPU コアより多くない場合にのみ実行を許可します。または、スレッドがしばらく「スタック」した場合。あなたは立ち往生します。また、それらを効果的に使用していないようです。ポーリングループは、多くの不必要な CPU サイクルを消費します。

これを回避するには、SerialPort クラスの機能を活用します。

  • 送信スレッドは必要ありません。シリアル ポート ドライバーにはバッファーがあり、データがバッファーに収まると、Write() 呼び出しはすぐに戻ります。メインスレッドからの書き込みは問題ありません。
  • 必ずしも受信スレッドは必要ありません。シリアル ポートには既に 1 つあり、DataReceived イベントを実行します。データを送信したときに開始したタイマーをバンプできます。
  • SerialPort には既に ReadTimeout プロパティがあります。これを受信スレッドで使用して、Read() 呼び出しをタイムアウトにすることができます。

Sleep() はシリアル ポートに干渉しません。シリアル ポートのドライバーはハードウェア割り込みを使用してデータを読み取ります。

于 2010-05-11T19:37:46.137 に答える