2

I am working on a serial port related application. While using DataReceived event of SerialPort I need to update a textbox with the received bytes:

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

    Invoke(new EventHandler(AddReceivedPacketToTextBox));       
}

So I use Invoke to update the textbox. But there is a big problem. When I try to close connection, my UI gets freezed, I think this is becase Invoke is doing somehing perhaps.

A friend said I should use RequiredInvoke, but I have no idea what he ment really. How can I close the connection without messing up the invoke and UI thread?

Here is my close method:

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

UPDATE

As Hans said I changed Invoke to BeginInvoke but now its a bit worse, my application stops working due to InvalidOperationException because the collection _readBuffer was modified (Thats what the detail says in VS)

Here is my code for adding text to textbox:

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??         
}

2nd Update

I still have the problem, changing the Invoke() to BeginInvoke didn;t help. I also tried to add disconnect to form closing event nu success...anytime I close my form it gets stock (I mean its parent form, because this form that has access to serialport is being called from another form`.

I mean I figured out that the UI gets locked only in 2 cases: If I clock a button which calls Connection.Close() also if I try to close the form, the parent form will throw exception that some objects are disposed.

I call the serial form like this from the parent form:

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; 
}

Could this be the problem?!

4

2 回答 2

7

是的,此代码在 Close() 调用中导致死锁的可能性非常高。在 DataReceived 事件处理程序停止运行之前,串行端口无法关闭。但在 UI 线程空闲并泵送消息循环之前,Invoke() 调用无法完成。它不是空闲的,它卡在 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 回答