使用Control.Invoke死锁?

时间:2012-05-15 13:50:01

标签: c# winforms task-parallel-library

我正在使用VS2010 Ultimate中的TPL构建应用程序。我运行应用程序的大多数时候,当我从UI的线程调用DoRepresentation()时,它变得没有响应。

void DoRepresentation()
{
  Parallel.ForEach(cgs, loopOptions, g =>
  {
    UpdateRepresentation(g);
  });
}

void UpdateRepresentation(object g)
{
  view.Invoke(new Action(() =>
  {
    representation = new MyRepresentation(g);
  }));
}

我不知道为什么应用程序变得反应迟钝。我有僵局吗?

MyRepresentation 里面我做了一些OpenGL调用。

view 是Form1中的一个控件(主窗体)。

当应用程序无响应时,我从VS IDE暂停它,这是我得到的信息

在“并行任务”窗口中,我得到以下信息:

ID  Status       Message<br>
1    ?Waiting   Task1 is waiting on object: "Task2"<br>
2    ?Waiting   No waiting information available<br>

在“调用堆栈”窗口中,我得到以下内容:

[In a Sleep, wait, or join]<br>
[External Code]<br>
Test.dll!Render.DoRepresentation()<br>
App1.exe!Form1.Button1_Click<br>

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:7)

是的,你正陷入困境。 Parallel.ForEach()做的是它使用一个或多个线程运行迭代,包括当前线程,然后阻塞当前线程直到所有迭代完成。

这意味着如果从UI线程调用DoRepresentation(),则会出现死锁:UI线程正在等待其他线程上的迭代完成,而其他线程正在等待Invoke()到完成,如果UI线程被阻止则不会发生。

此外,在您的情况下,使用Parallel.ForEach()没有任何意义(假设这是您的实际代码):您在UI线程上运行new MyRepresentation()

我不明白代码究竟在做什么(它似乎在每次迭代中覆盖representation),但我认为你应该从后台线程运行ForEach()。这意味着DoRepresentation()将在完成其工作之前返回,因此Invoke()将正常工作。

一般来说,长时间阻止UI线程并不是一个好主意,所以你应该在另一个线程上运行任何耗时的代码。

答案 1 :(得分:1)

您可以使用Invoke方法保留的BeginInvoke。如果你仍然需要那么你可以锁定一个对象,并确保在实现之前无法从其他线程访问它。

使用Begin Invoke Method

void UpdateRepresentation(object g)
{
  view.BeginInvoke( new Action(() =>
  {
    representation = new MyRepresentation(g);
  }));
}

使用锁定

void UpdateRepresentation(object g)
{
lock(this) 
{
 view.Invoke(new Action(() =>
  {
    representation = new MyRepresentation(g);
  }));
}

}

答案 2 :(得分:0)

此评论适用于我的特定应用,它是C#中的Windows应用:使用锁定对我来说也不起作用,应用程序只是冻结了。 BeginInvoke有效,但我不喜欢异步更新UI控件的效果。

我最终将主进程作为一个单独的线程(System.Threading.Tasks.Task)启动,它将启动并立即让我重新控制主线程。之后,在等待其他几个任务在循环中结束执行时,我也最终必须插入此行:System.Windows.Forms.Application.DoEvents()以使系统能够处理队列中等待的所有消息。现在它适用于我的应用程序。可能还有另一种方法可以为这只猫提供皮肤,但它现在可以使用了。