2

私はシリアルポート関連のアプリケーションに取り組んでいます。DataReceivedのイベントを使用している間SerialPort、受信したバイトでテキストボックスを更新する必要があります。

private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    var data = Connection.ReadExisting();
    _readBuffer.Add(indata);

    Invoke(new EventHandler(AddReceivedPacketToTextBox));       
}

だから私Invokeはテキストボックスを更新するために使用します。しかし、大きな問題があります。接続を閉じようとすると、UIがフリーズします。これは、Invokeおそらく何かをしているためだと思います。

友人RequiredInvokeが私が使うべきだと言ったが、彼が本当に何を言っているのか私にはわからない。呼び出しスレッドとUIスレッドを台無しにせずに接続を閉じるにはどうすればよいですか?

これが私の閉じる方法です:

private void DisconnectFromSerialPort()
{
    if (Connection != null && Connection.IsOpen)
    {
        Connection.Close();            
    }
}

アップデート

ハンスが私に変更Invokeしたと言ったが、今は少し悪くなっているが、コレクションが変更されたためBeginInvokeにアプリケーションが機能しなくなった(VSで詳細が述べていること)InvalidOperationException_readBuffer

テキストボックスにテキストを追加するための私のコードは次のとおりです。

private void AddReceivedPacketToTextBox(object sender, EventArgs e)
{

    foreach (var i in _readBuffer)
        tbIn.Text += string.Format("{0:X2} ", i);

    tbIn.Text += Environment.NewLine;

    ScrollToBottom(tbIn);

    label4.Text = _receivedPackets.ToString();
    _receivedPackets++;

    _readBuffer.Clear(); //Possibly because clearing collection gets out of sync with BeginInvoke??         
}

2回目の更新

私はまだ問題を抱えており、を変更しInvoke()ましたBeginInvoke;助けにはなりません。また、フォームを閉じるイベントnu successに切断を追加しようとしました...フォームを閉じるといつでも在庫があります(シリアルポートにアクセスできるこのフォームは別のフォームから呼び出されているため、親フォームを意味します`。

つまり、UIがロックされるのは2つの場合だけであることがわかりました。Connection.Close() フォームを閉じようとしても呼び出すボタンをクロックすると、親フォームは一部のオブジェクトが破棄されるという例外をスローします。

親フォームから次のようなシリアルフォームを呼び出します。

public DebugForm DebugForm;

private void button1_Click(object sender, EventArgs e)
{
    if (DebugForm != null)
    {
        DebugForm.BringToFront();
        return;
    }

    DebugForm = new DebugForm();
    DebugForm.StartPosition = FormStartPosition.CenterScreen;
    DebugForm.Closed += delegate
                             {
                                 WindowState = FormWindowState.Normal;
                                 DebugForm = null;
                             };

    DebugForm.Show();
    WindowState = FormWindowState.Minimized; 
}

これが問題でしょうか?!

4

2 に答える 2

7

はい、このコードはClose()呼び出しでデッドロックを引き起こす可能性が非常に高いです。シリアルポートは、DataReceivedイベントハンドラーの実行が停止するまで閉じることができません。ただし、Invoke()呼び出しは、UIスレッドがアイドル状態になり、メッセージループをポンピングするまで完了できません。アイドル状態ではなく、Close()呼び出しでスタックします。そのため、イベントハンドラーは、Invoke()呼び出しでスタックしているため進行できず、メインスレッドはClose()呼び出しでスタックしているため、進行できません。デッドロックシティです。

最善の回避策は、代わりにBeginInvoke()を使用することです。これにより、イベントハンドラーがブロックされません。シリアルポートをまったく閉じないことも別の回避策です。これは、プログラムの終了時にWindowsが自動的に処理するため問題ありません。一般に、デバイスがデータの送信でビジー状態のときにシリアルポートを閉じると、データが失われ、必然的にデータが失われます。コードをデバッグしているが、本番環境で通常発生することを望んでいない場合は問題ありません。

于 2012-10-16T12:04:30.367 に答える
-1

この問題は、タイマーを追加することで解決できます。

  bool formClosing = false;
    private void Connection_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
      if (formClosing) return;
      _buffer = Connection.ReadExisting();
      Invoke(new EventHandler(AddReceivedPacketToTextBox));
    }
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
      base.OnFormClosing(e);
      if (formClosing) return;
      e.Cancel = true;
      Timer tmr = new Timer();
      tmr.Tick += Tmr_Tick;
      tmr.Start();
      formClosing = true;
    }
    void Tmr_Tick(object sender, EventArgs e)
    {
      ((Timer)sender).Stop();
      this.Close();
    }

MSDNのJohnWeinに感謝します

于 2012-10-20T14:43:04.067 に答える