问题
I'm building an app using TPL in VS2010 Ultimate. The most of the times I run the app it becomes unresponsive when I Call DoRepresentation() from the UI's thread.
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.
view is a Control inside Form1 (the main form).
When the app become unresponsive I pause it from the VS IDE and here's the info I get
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.
回答1:
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.
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.
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.
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. This means DoRepresentation()
will return before it finishes its work and so Invoke()
will work correctly.
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.
回答2:
you can use the BeginInvoke insteed of Invoke Method. 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);
}));
}
}
回答3:
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.
BeginInvoke
worked, but I didn't like the effect of having UI controls being updated asynchronously.
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. 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. Now it works right for my application. There might be another way to skin this cat, but it works now.
来源:https://stackoverflow.com/questions/10602184/deadlock-using-control-invoke