如何使用后台工作程序(或任何其他解决方案)来停止冻结我的 GUI

本文关键字:冻结 GUI 我的 解决方案 其他 后台 何使用 工作程序 任何 | 更新日期: 2023-09-27 17:56:45

我正在尝试修复 Winforms 应用程序中 GUI 趋于锁定的问题。

当前解决方案:应用程序被写入从串行端口读取并写入 GUI 中的富文本框。现在,响应可以是单个响应,也可以是基于输入的高速连续流式传输。

截至目前,我正在使用从设备接收内容时的内容更新文本框,即使用从串行端口接收数据时触发的事件处理程序。

后台工作者是解决问题的唯一解决方案吗?如果是,如何重构解决方案以适应此更改?(我知道后台工作者无法访问 GUI)。如果没有,有没有更好的解决方案?

编辑代码:这是常规代码流

//Function triggered when data received from serial port
   private void DataReceived(object sender, EventArgs e)
        {
             while (serialPort1.BytesToRead > 0 && serialPort1.IsOpen)
            {
                 //calls to several processing functions
                  //which then call a function writetoTextBox and pass the data to write
           }
        }
//write to textbox
void writeToTextBox(inputdata)
{
// write to textbox. 
//If user has asked to write to file. Open a file dialog to get file name and write to it
// as well. As of now when the data rate is high, this part doesnt get the time to respond
//as the GUI locks up the thread
}

免责声明:我对winforms和C#本身相对较新。因此,任何建议将不胜感激!

如何使用后台工作程序(或任何其他解决方案)来停止冻结我的 GUI

您可以在此处使用多种方法:

  • BackroundWorker(您建议的)
  • Task对象和async/await:异步和等待的异步编程 - MSDN
  • 基于Thread滚动您自己的后台辅助角色:线程类 - MSDN

我建议使用 async/await 方法,因为 MS 已经努力确保一切保持同步,但您可以根据您的应用程序决定您想要什么。

若要确保可以访问 UI 线程,必须使用 Invoke 方法:从另一个线程更新 GUI - SO

滚动自己的Thread的示例如下:

  • 将所有串行处理代码放在它自己的对象/类中
  • 主线程实例化对象 从它自己的线程上启动接收方法
  • 收到数据后,将事件引发回主线程

若要避免锁定 UI,可以执行以下操作:

private delegate void writeToTextBoxDelegate(List a, List b);
private async void DataReceived(object sender, EventArgs e)
{
    while (serialPort1.BytesToRead > 0 && serialPort1.IsOpen)
    {
        await Task.Factory.StartNew(() =>
        {
            // Do whatever work you want to do here.
            // When you're all done, call the following line.
            textBox.Invoke(
                new writeToTextBoxDelegate(writeToTextBox),
                new object[] { a, b }
            );
        });
    }
}

如果您在while循环中所做的额外工作并不重要,您可能需要移动

await Task.Factory.StartNew(() => { });

while循环之外。目标是不要太严重地占用Task,因为允许运行Task的线程数量是有限的。

调用Invoke的另一种方法是:

private delegate void writeToTextBoxDelegate(List a, List b);
private void writeToTextBox(List a, List b)
{
    if (textBox.InvokeRequired)
    {
        textBox.Invoke(new writeToTextBoxDelegate(writeToTextBox),
            new object[] { a, b });
        return;
    }
    // Your custom write-to-textbox code here.
}

然后,您可以简单地从任何地方调用writeToTextBox,它将处理调用本身。

为了避免重新编码所有项目,您可以尝试使用 Application.DoEvents() .

void writeToTextBox(inputdata)
{
    /*your code */
     Application.DoEvents();
}

您可以在那里看到该方法的描述和一个小示例。希望这有帮助!