使用Parallel BeginInvoke时可以工作,而使用Invoke时无法工作 - c# 4.0

3
当我在AddListBoxItem函数中使用invoke时,软件会变得没有响应和冻结,但如果我使用 BeginInvoke 就可以工作。为什么会发生这种情况? Visual Studio 2010,C# 4.0
 private void button2_Click(object sender, EventArgs e)
{
    var watch = Stopwatch.StartNew();
    Parallel.For(2, 20, (i) =>
    {
        var result = SumRootN(i);
        AddListBoxItem("root " + i + " : " + result);
    });
    AddListBoxItem(watch.ElapsedMilliseconds.ToString());            
}

private delegate void AddListBoxItemDelegate(object item);

private void AddListBoxItem(object item)
{
    if (this.listBox1.InvokeRequired)
    {
        this.listBox1.Invoke(new AddListBoxItemDelegate(this.AddListBoxItem), item);
    }
    else
    {
        this.listBox1.Items.Add(item);
    }
}
3个回答

3
您的UI线程将等待Parallel.For完成后才会继续。这意味着在完成之前它无法处理任何其他UI消息。
现在,当工作线程调用Invoke时,它们会等待UI线程处理委托后才会继续。因此,它们基本上正在等待UI线程空闲。
因此,您有一个死锁-UI线程正在等待任务,而任务正在等待UI线程... BeginInvoke有效是因为任务线程不会等待在UI线程中处理委托。
我建议您首先不要在UI线程中调用Parallel.For。无论如何,您都会阻塞UI直到它完成,这不是一个好主意。在后台线程中完成整个操作-然后如果需要仍然可以使用Invoke,并且在计算过程中UI仍将保持响应。

我该如何在不同的线程中调用它?我的意思是使用Parallel.For调用它? - Furkan Gözükara
@MonsterMMORPG:以与任何其他方式相同的方式从BackgroundWorker、使用ThreadPool.QueueUserWorkItem甚至Task.Factory.StartNew中的不同线程完成整个事情 - Jon Skeet
我认为任务更好,谢谢你的回答。现在我明白为什么会变得无响应了 :) - Furkan Gözükara
我认为将其放入任务中最有可能不需要额外的线程。 - H H
@HenkHolterman:可能与使用ThreadPool.QueueUserWorkItem相同,但绝对更加简洁。 - Jon Skeet

3
似乎你在造成UI线程死锁。这很有道理,因为当你的button2_Click没有退出时,For也不会完成,特别是在button2_Click完成之前,无法处理任何消息循环事件。如果你在另一个线程上,Invoke会使用消息循环事件,并且在该项被处理之前不会返回-因此永远不会完成任何操作-而且For/button2_Click将永远不会完成。
通过使用BeginInvoke,你只需将此工作排队-BeginInvoke立即返回。这意味着For可以完成,从而使button2_Click完成,然后允许处理消息循环事件(更新UI)。

1

我认为这是因为在上面的Click事件中,主线程被阻塞,等待完成AddListBoxItem,而AddListBoxItem又在等待button2_click事件返回。
你不应该在UI中放置控制器逻辑,所以主要问题是Click没有回调不同线程中的逻辑。

实现一个线程来处理你的逻辑后,你的GUI就不会被阻塞,并且可以在任何情况下轻松刷新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接