【问题标题】:WinForm Application stop running and freeze windowsWinForm 应用程序停止运行并冻结窗口
【发布时间】:2019-11-25 16:33:53
【问题描述】:

我是 Windows 开发新手。 我开发了一个与串行设备通信并在图表上绘制数据的 WinForm 应用程序。 该应用程序应每天 7 月 7 日 24 小时运行。代码正确执行,但在执行几个小时后,UI 因操作系统不响应而冻结(我必须关闭电脑并重新启动它)。

串行类(使用System.IO.Ports)在单独的线程上执行读写操作。 这种情况让我想到了从我的串行类到 UI 的不正确的跨线程调用。阅读 Microsoft 文档和其他问题,我认为我修复了错误,但没办法,该应用程序继续冻结操作系统。

写入操作每 100 毫秒通过串行向开发板发送一个请求字符,我正在使用 System.Threading.Timer 来执行此操作,因为此操作不会与 UI 交互。

//Initialization

WriteTimer = new Timer(Write, COMport.IsOpen, 5000, 100);

// callback function

public static void Write(object state)
{
    if ((bool) state)
    {
        try
        {
            COMport.Write("^");
        }
        catch (Exception exc)
        {
            ErrorLogger.WriteTxtLog(DateTime.Now,exc.ToString());
        }
    }
}

串口板以10字节的字符串回复请求字符,读取消息我使用DataReceived事件处理程序形式IO.Ports,这也是在单独的线程上执行,接收到的数据将进行详细说明,然后使用在主表单类上声明的带有 args 的事件处理程序委托传递给 UI。我将表单控件传递给串行类以进行安全的线程调用。

// FormControl is passed in the constructor of the serial class: 

public Form1 FormControl;

private void COMport_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    SerialPort port = (SerialPort) sender;

    byte[] buffer = new byte[10];

    try
    {
        for (int i = 0; i < buffer.Length && buffer[i]!=3 ; i++)
        {
             buffer[i] = (byte) port.ReadByte();
        }

        SerialDataArgs args = checkDataReceived(buffer);

        if (!args.error)
        {
           FormControl.Invoke(FormControl.drItem, new SerialDataArgs(...));

        }
        else
        {   
            FormControl.Invoke(FormControl.drItem, new SerialDataArgs(0,0,true));
            ErrorLogger.WriteTxtLog(DateTime.Now, "");
        }
    }
    catch (Exception exc)
    {
        ErrorLogger.WriteTxtLog(DateTime.Now, exc.ToString());
    }
}

UI 线程(Form1 类):

SerialClass Serial = null;

public delegate void DataReceived(SerialDataArgs args);
public DataReceived drItem;


void ConnectCOM()
{
    // COM READ 
    if (Serial == null)
    {
        Serial = new SerialClass(param.comName, this //this should be the FORM CONTROL);

        if (Serial.Open)
        {
            ...
            drItem += HandleSeriaData;
        }
        else
        {
            ...
        }
    }
}

//EventHandler
private void HandleSeriaData(SerialDataArgs args)
{
    if (!args.error)
    {
        Work(args...); // in the work method i'll update labels, drawGraph,....
    }
    else
    {
        if (!Serial.Open) RecoverySerial();
    }
}

也许错误不在这里,但在 Windows 崩溃之前,它似乎是我唯一感兴趣的部分。抱歉我的英语不好,希望我提出了一个正确/不重复的问题。

【问题讨论】:

    标签: winforms


    【解决方案1】:

    使用Control.BeginInvoke 而不是Control.Invoke

    根据我的经验,System.IO.Ports.SerialPort 的问题通常只会导致 UI 冻结,而不是操作系统,并且是由尝试使用 Control.Invoke 和 UI 线程从 SerialPort .DataReceived 事件更新 UI 时出现死锁引起的本身尝试访问 SerialPort 对象,例如关闭它。在这种情况下,当 UI 线程正在等待 SerialPort DataReceived 线程完成并且 DataReceived 线程正在等待 UI 线程完成 Control.Invoke 时,可能会出现死锁。为了克服这些问题,最好使用Control.BeginInvoke,这样DataReceived线程就不会等待UI线程。

    使用lock同步访问共享资源

    不同线程访问同一个对象时,使用lock同步数据访问。没有同步的数据访问会导致各种奇怪的问题。

    检查是串口驱动还是连接问题

    当 UI 冻结并且操作系统变得无响应时,请尝试移除串行端口设备并查看操作系统是否变得响应。如果是这样,问题可能不在应用程序上,而在串口驱动程序或连接上。

    希望对你有帮助

    【讨论】:

    • 谢谢,我更正了我的代码并找出了问题所在。它不是在串行通信上,也不是无效的跨线程操作,而是在另一个 FTDI 设备或驱动程序上。
    猜你喜欢
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多