是否可以使用ISynchronizeInvoke.BeginInvoke()重载线程?

时间:2009-09-02 06:55:09

标签: c# winforms multithreading

我的问题是:

我有两个线程,我的UI线程和一个工作线程。我的工作线程在一个单独的类中运行,该类由表单实例化,它将自己作为ISynchronizeInvoke传递给worker类,然后在该接口上使用Invoke来调用它的事件,这些事件提供状态更新UI以供显示。这很有效。

我注意到我的后台线程似乎运行缓慢,所以我将调用更改为InvokeBeginInvoke,认为“我只是提供进度更新,它不需要要完全同步,不要做任何伤害“除了现在我对进度更新感到奇怪。我的进度条更新,但标签的文本没有更新,如果我更改到另一个窗口并尝试更改回来,它就像UI线程被锁定一样,所以我想知道是否可能是我的进度调用(这种情况非常发生)经常)重载UI线程,以至于它永远不会处理消息。这有可能吗,或者还有其他工作在这里吗?

2 个答案:

答案 0 :(得分:3)

你明确地重载了​​UI线程。

在您的第一个示例中,您(在幕后)向UI线程发送消息,等待处理(这是调用的目的,最终依赖于SendMessage),然后发送另一个。与此同时,其他消息可能已入队(例如WM_PAINT消息)并已处理。

在第二个示例中,通过使用BeginInvoke(最终依赖于PostMessage),您大量排队消息队列中的大量消息,消息泵必须按顺序处理。当然,当它处理成千上万条消息时,它无法处理操作系统消息(WM_PAINT等),这会使您的UI看起来“冻结”

您可能提供了太多状态更新;试着降低反馈水平。

如果您想更好地了解消息在Windows中的工作方式,this就是您的起点。

答案 1 :(得分:1)

一些想法;

  • 尝试批量处理您的更新;例如,循环中每次迭代都没有更新点;根据速度,可能每50/500。在列表的情况下,您将缓冲在本地列表变量中,通过Invoke / BeginInvoke获取列表,并在UI上处理缓冲区螺纹
  • 变量捕获;如果你使用BeginInvoke和匿名方法,你可能会遇到问题......我将在下面添加一个例子
  • 使UI更新更有效 - 尤其是如果您正在处理列表;某些控件(尤其是基于列表的控件)有一对方法,如BeginEdit / EndEdit,当您进行大量更新时,它会停止重绘UI;相反,它会等到End*被调用

捕获问题...想象(工人):

List<string> stuff = new List<string>();
for(int i = 0 ; i < 50000 ; i++) {
    stuff.Add(i.ToString());
    if((i % 100) == 0) {
        // update UI
        BeginInvoke((MethodInvoker) delegate {
            foreach(string s in stuff) {
                listBox.Items.Add(s);
            }
        });
    }
}

您是否注意到两个线程都在与stuff交谈? UI线程可以迭代它,而工作线程(已经运行过BeginInvoke)继续添加。这可能会导致问题。通常不会出现性能问题(除非您捕获异常并花费很长时间来记录它们),但肯定会出现问题。这里的选项包括:

  • 使用Invoke同步运行更新
  • 每次更新创建一个新的缓冲区,这样两个线程永远不会有相同的列表实例(你需要仔细查看范围内的变量以确保,但是)