0

SerialPort を停止しようとしているときに奇妙な動作が発生しています。DataReceived イベントは、サブスクライブ解除後および呼び出し後も発生し続けcloseます。(StopStreaming次のコードを参照)。その結果、イベント ハンドラー コードで、「ポートが閉じられています」というメッセージとともに InvalidOperationException が発生します。

私は何が欠けていますか?ポートを閉じてイベントを停止する正しい方法は何ですか?

編集:コードを実行するたびにこのエラーが発生します。したがって、これはランダムに発生する競合状態ではなく、コードが完全に壊れていることを示す体系的な問題です! しかし、私は方法を見ることができません...

private SerialPort comPort = new SerialPort();

public override void StartStreaming()
{
  comPort.Open();
  comPort.DiscardInBuffer();
  comPort.DataReceived += comPort_DataReceived;
}

public override void StopStreaming()
{
  comPort.DataReceived -= comPort_DataReceived;
  comPort.Close();
  isStreaming = false;
}

private void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  if (e.EventType == SerialData.Chars)
  {
    SerialPort port = (SerialPort)sender;
    int N = comPort.BytesToRead;
    for (; N > 0; N--)
    {
      byte b = Convert.ToByte(comPort.ReadByte());
      //... process b
    }
  }
}

編集:提案に従って、StopStreamingコードを次のように変更しました:

public override void StopStreaming()
{
  comPort.DataReceived -= comPort_DataReceived;
  Thread.Sleep(1000);
  comPort.DiscardInBuffer();
  Thread.Sleep(1000);
  comPort.Close();
  isStreaming = false;
}

今はうまくいっているようですが、私はそれほど幸せではありません。プログラムにスリープ期間を挿入するのではなく、コールバックを削除するより効果的な方法があればいいのにと思います。

4

4 に答える 4

5

DataReceived イベント ハンドラーがスレッドプール スレッドで呼び出されます。そして、そうです、彼らはあなたのコードを予測不可能なタイミングで実行するという厄介な習慣を持っています。それは瞬時ではありません。そのため、デバイスがアクティブにデータを送信している場合、Close() 呼び出しと競合し、閉じた後に実行されることは避けられません。サブスクライブを解除しても修正されません。スレッドプール スレッドは既にターゲット メソッドを取得しています。

この問題を引き起こすために何をしているのかを理解してください。デバイスがデータを送信している間にポートを閉じています。それは素晴らしいことではありません。データが失われることが保証されています。ただし、実際にはデータを気にしないため、コードをデバッグしているときに発生する可能性は低くありません。

対応策は、ハンドシェイクをオフにして、デバイスが何も送信できないようにすることです。入力バッファを破棄します。次に、1 ~ 2 秒間スリープして、進行中のスレッドプール スレッドの実行が完了したことを確認します。次に、ポートを閉じます。非常に実用的な方法は、単純にポートを閉じないことです。プロセスが終了すると、Windows がポートを処理します。

于 2012-12-14T10:59:29.593 に答える
2

マルチスレッドの問題のようです。

SerialPort.DataReceivedスレッドプールからワーカースレッドで発生します。で発生したスレッドとは異なり、スレッドからポートを閉じていますSerialPort.DataReceived

InvalidOperationExceptionこの問題を解決するために、同期コードを処理または記述できます。

アップデート。

問題は、デバイスがデータを集中的に送信する場合、SerialPortスレッドプールにますます多くの作業項目をキューに入れることです。コードが以前にスリープする場合でもClose、それだけでは不十分な場合があります。残念ながら、SerialPort実装は醜いです。「データでスパムを止めてください」と言うオプションはありません。

したがって、具体的な解決策は、デバイスのプロトコルとハンドシェイクパラメータに依存します。

于 2012-12-14T10:25:12.553 に答える