gpt4 book ai didi

multithreading - 高性能的 Winforms 更新

转载 作者:行者123 更新时间:2023-12-03 13:14:37 24 4
gpt4 key购买 nike

让我用一些背景信息来设置这个问题,我们有一个长时间运行的进程,它将在 Windows 窗体中生成数据。因此,显然需要某种形式的多线程来保持表单响应。但是,我们还要求表单每秒更新多次,同时仍保持响应。

这是一个使用后台工作线程的简单测试示例:

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int reportValue = (int)e.UserState;
label1.Text = reportValue;
//We can put this.Refresh() here to force repaint which gives us high repaints but we lose
//all other responsiveness with the control

}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int x = 0; x < 100000; x++)
{
//We could put Thread.Sleep here but we won't get highest performance updates
bw.ReportProgress(0, x);
}
}

请查看代码中的注释。另外,请不要质疑我为什么要这个。问题很简单,我们如何在保持响应性的同时在更新表单时实现最高保真度(最多重绘)?强制重绘确实给了我们更新,但我们不处理 Windows 消息。

我也尝试放置 DoEvents 但这会产生堆栈溢出。我需要的是某种说法,“如果你最近没有处理任何 Windows 消息”。我还可以看到,可能需要稍微不同的模式来实现这一点。

看来我们需要处理几个问题:
  • 通过非 UI 线程更新表单。这个问题有很多解决方案,例如调用、同步上下文、后台工作模式。
  • 第二个问题是用太多更新来淹没表单,这会阻止消息处理,这是我的问题真正关心的问题。在大多数示例中,这是通过任意等待减慢请求或仅每 X% 更新一次来轻松处理的。这些解决方案都不适合现实世界的应用程序,也不符合最大更新同时响应标准。

  • 我对如何处理这个问题的一些初步想法:
  • 在后台工作人员中对项目进行排队,然后在 UI 线程中分派(dispatch)它们。这将确保每个项目都被绘制,但会导致我们不想要的滞后。
  • 也许使用 TPL
  • 也许在 UI 线程中使用计时器来指定刷新值。通过这种方式,我们可以以我们可以处理的最快速度抓取数据。它将需要跨线程访问/共享数据。


  • 更新,我已经更新为使用 Timer 来读取具有后台工作线程更新的共享变量。现在由于某种原因,这种方法产生了良好的表单响应,并且还允许后台工作人员以大约 1,000 倍的速度更新。但是,有趣的是它只有 1 毫秒的准确度。

    因此,我们应该能够更改模式以读取当前时间并从 bw 线程调用更新,而无需计时器。

    这是新模式:
    //Timer setup
    {
    RefreshTimer.SynchronizingObject = this;
    RefreshTimer.Elapsed += RefreshTimer_Elapsed;
    RefreshTimer.AutoReset = true;
    RefreshTimer.Start();
    }

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
    for (int x = 0; x < 1000000000; x++)
    {
    //bw.ReportProgress(0, x);
    //mUiContext.Post(UpdateLabel, x);
    SharedX = x;
    }
    }

    void RefreshTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
    label1.Text = SharedX.ToString();
    }

    更新 在这里,我们有了不需要计时器且不会阻塞线程的新解决方案!我们使用这种模式在计算和更新保真度方面实现了高性能。不幸的是,滴答 TickCount 的准确度仅为 1 MS,但是我们可以为每个 MS 运行一批 X 更新,以获得比 1 MS 计时更快的速度。
       void bw_DoWork(object sender, DoWorkEventArgs e)
    {
    long lastTickCount = Environment.TickCount;
    for (int x = 0; x < 1000000000; x++)
    {
    if (Environment.TickCount - lastTickCount > 1)
    {
    bw.ReportProgress(0, x);
    lastTickCount = Environment.TickCount;
    }
    }
    }

    最佳答案

    尝试以比用户跟踪进度更快的速度报告进度是没有意义的。

    如果您的后台线程发布消息的速度超过了 GUI 处理它们的速度,(并且您有所有的症状 - GUI 对用户输入的 react 不佳,DoEvents 失控递归),您必须以某种方式限制进度更新。

    一种常见的方法是使用主线程表单计时器以足够小的速率更新 GUI,以使用户看到可接受的进度读数。您可能需要一个互斥锁或临界区来保护共享数据,但如果要监视的进度值是 int/uint,则这不是必需的。

    另一种方法是通过强制线程阻塞事件或信号量直到 GUI 空闲来扼杀线程。

    关于multithreading - 高性能的 Winforms 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21643584/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com