使用后台线程的结果更新(大量)GUI项目

时间:2011-07-04 08:56:15

标签: c# multithreading user-interface

我有一个TreeView控件,其中填充了项目(一个用于特定目录中的每个文件),以及一个在后台运行的Thread,它会使用额外的数据缓慢填充树中的每个节点。

然后我需要TreeViewNode根据结果更新其Text字段。

我已经尝试在每个项目上对每个项目使用BeginInvoke,但是这会导致GUI线程无响应,因为后台工作程序正在处理的剪切节点数。 C# - Updating GUI using non-main Thread

public static void InvokeIfRequired(this System.Windows.Forms.Control c,
                                    Action action) {
    if (c.InvokeRequired) {
        c.Invoke((Action)(() => action()));
    }
    else {
        action();
    }
}

我已经尝试将几个节点更新批处理为GUI的单个工作项,并使用BeginInvoke一次更新几个,在此期间后台线程等待信号继续其工作,但这也导致GUI在Invoked方法期间变得没有响应(似乎更新TreeViewNode是一项非常昂贵的操作?)

最后,我在GUI线程上创建了一个队列,该队列由工作线程填充,并在应用程序空闲时轮询,这很好用,但感觉就像一个完整的黑客。

using System;
using System.Runtime.InteropServices;

namespace System
{
    struct PointAPI
    {
        public Int32 x;
        public Int32 y;
    }

    struct WindowsMessage
    {
        public Int32 hwnd;
        public Int32 message;
        public Int32 wParam;
        public Int32 lParam;
        public Int32 time;
        public PointAPI pt;
    }

    static class IdlePolling
    {
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool PeekMessage(ref WindowsMessage lpMsg,
                                                Int32 hwnd,
                                                Int32 wMsgFilterMin,
                                                Int32 wMsgFilterMax,
                                                Int32 wRemoveMsg);
        static public bool HasEventsPending()
        {
            WindowsMessage msg = new WindowsMessage();
            return PeekMessage(ref msg, 0, 0, 0, 0);
        }
    }
}

...
Application.Idle += mainForm.OnIdle;
...

    public partial class MainWindow : Form
    {
        ...
        public void OnIdle(object sender, EventArgs e)
        {
            while (IdlePolling.HasEventsPending() == false)
            {
                ConsumeGUIUpdateItem();
                Thread.Sleep(50);
            }
        }
    }

那么更新大量gui项目的“正确”方法是什么,这不会导致GUI线程在此过程中挂起。

1 个答案:

答案 0 :(得分:1)

russ,那里有很好的尝试。总结:我认为修复是 调用 批处理而不是 begininvoke ()。让我解释一下:

当我向WPF中的数据网格添加许多项目时,我遇到了类似的问题,其中工作人员开始调用(或WPF中的等效项)到UI线程。我也发现在高调用下,新项目没有按照应有的高效插入。 asnc调用代表了这个特殊设置的高开销百分比,这很有趣。

最后,我采用了一种更简单的方法,只是插入了批次(就像你已经完成的那样),而是通过异步线程编组插入它们,而不是仅仅将WPF等同于同步 调用()而不是 async BeginInvoke ()。

您可能知道,每次 begininvoke ()时,.net会在 Windows消息泵中放置一条消息来处理该呼叫。但是,如果您的应用程序忙于处理其他消息,则插入可能需要一段时间。类似地,过多的begininvokes()阻碍了其他Windows消息泵消息的处理。最终,当发送太多窗口时,可能会丢弃多余的内容,因此可能永远不会调用您的方法。无论你知道c ++或.net,都是如此。

最终,Windows在UI线程中一次只能执行一件事,无论是执行典型的GUI工作还是处理异步方法。

欢呼声