简体   繁体   English

使用Control.Invoke死锁?

[英]Deadlock using Control.Invoke?

I'm building an app using TPL in VS2010 Ultimate. 我正在VS2010 Ultimate中使用TPL构建应用程序。 The most of the times I run the app it becomes unresponsive when I Call DoRepresentation() from the UI's thread. 在我运行应用程序的大多数时候,当我从UI线程调用DoRepresentation()时,该应用程序将变得无响应。

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

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

I don't know why the app is becoming unresponsive. 我不知道为什么该应用程序变得无响应。 Am I having a deadlock? 我有僵局吗?

Inside MyRepresentation I do some calls to OpenGL. MyRepresentation内部,我对OpenGL进行了一些调用。

view is a Control inside Form1 (the main form). view是Form1(主窗体)内部的一个控件。

When the app become unresponsive I pause it from the VS IDE and here's the info I get 当应用程序无响应时,我从VS IDE暂停它,这是我得到的信息

In the "Parallel Tasks" window I get the following: 在“并行任务”窗口中,我得到以下信息:

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

In the "Call Stack" window I get the following: 在“调用堆栈”窗口中,我得到以下信息:

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

Any help will be appreciated. 任何帮助将不胜感激。

Yes, you are having a deadlock. 是的,您遇到了僵局。 What Parallel.ForEach() does is that it runs the iterations using one or more threads including the current one and then blocks the current thread until all iterations are complete. Parallel.ForEach()作用是使用一个或多个线程( 包括当前线程Parallel.ForEach()运行迭代,然后阻塞当前线程,直到所有迭代完成为止。

This means that if you call DoRepresentation() from the UI thread, you get a deadlock: the UI thread is waiting for iterations on other threads to finish, while those other threads are waiting for Invoke() to finish, which can't happen if the UI thread is blocked. 这意味着,如果从UI线程调用DoRepresentation() ,则会出现死锁:UI线程正在等待其他线程上的迭代完成,而那些其他线程正在等待Invoke()完成,这不会发生如果UI线程被阻止。

Also, in your case, using Parallel.ForEach() doesn't make any sense (assuming this is your actual code): you run new MyRepresentation() on the UI thread. 同样,在您的情况下,使用Parallel.ForEach()没有任何意义(假设这是您的实际代码):您在UI线程上运行了new MyRepresentation()

I don't understand what exactly is the code doing (it seems it overwrites representation in each iteration), but I think you should run ForEach() from a background thread. 我不了解代码的确切功能(似乎它覆盖了每次迭代中的representation ),但是我认为您应该从后台线程运行ForEach() This means DoRepresentation() will return before it finishes its work and so Invoke() will work correctly. 这意味着DoRepresentation()将在完成工作之前返回,因此Invoke()将正常工作。

In general, it's not a good idea to block the UI thread for a long time, so you should run any time-consuming code on another thread. 通常,长时间阻塞UI线程不是一个好主意,因此您应该在另一个线程上运行所有耗时的代码。

you can use the BeginInvoke insteed of Invoke Method. 您可以使用Invoke方法的BeginInvoke指令。 if you still need then you can lock an object and make sure that this will not be accessible from the other thread until its realized. 如果仍然需要,则可以锁定一个对象,并确保在实现之前无法从另一个线程访问该对象。

using the Begin Invoke Method 使用开始调用方法

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

Using the Lock 使用锁

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

}

This comment applies to my specific app, which is a Windows app in C#: Using a Lock did not work for me either, and the application just froze up. 此注释适用于我的特定应用程序,该应用程序是C#中的Windows应用程序:使用锁也不适合我,该应用程序只是冻结了。 BeginInvoke worked, but I didn't like the effect of having UI controls being updated asynchronously. BeginInvoke工作,但是我不喜欢异步更新UI控件的效果。

I ended up starting the main process as a separate thread ( System.Threading.Tasks.Task ), which would start and instantly give me back control of the main thread. 我最终以一个单独的线程( System.Threading.Tasks.Task )启动主进程,该进程将启动并立即将主线程的控制权交还给我。 Afterwards, while waiting for several other tasks to end execution in a loop, I also ended up having to insert this line: System.Windows.Forms.Application.DoEvents() to enable the system to process all messages waiting in the queue. 然后,在等待其他任务循环结束执行时,我还不得不插入以下行: System.Windows.Forms.Application.DoEvents()以使系统能够处理队列中所有等待的消息。 Now it works right for my application. 现在它适合我的应用程序。 There might be another way to skin this cat, but it works now. 可能还有另一种方法可以给这只猫蒙皮,但现在可以用了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM