1

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.

svick
  • 236,525
  • 50
  • 385
  • 514
Michelle
  • 601
  • 1
  • 8
  • 17
  • Can't you use `BeginInvoke` rather than `Invoke`? – Nick May 15 '12 at 13:52
  • No because I need to block until UpdateRepresentation() return. Because after the Parallel.ForEach() I do some other calls to some methods. – Michelle May 15 '12 at 13:56
  • Maybe a stupid question, but does View have a Dispatcher so you can do view.Dispatcher.Invoke()? – Davio May 15 '12 at 13:58
  • Does the constructor for `MyRepresentation` need to be called on the UI thread? – Nick May 15 '12 at 14:00
  • @Davio no it doesn't have a Dispatcher method. It inherits directly from System.Windows.Forms.Control – Michelle May 15 '12 at 14:05
  • @Nick yes because I'm doing some calls to the OpenGL API – Michelle May 15 '12 at 14:06
  • @Nick: Doesn't a call to `BeginInvoke` require some call to `EndInvoke`? http://stackoverflow.com/questions/1712741/why-does-asynchronous-delegate-method-require-calling-endinvoke – Thorsten Dittmar May 15 '12 at 14:14
  • @Michelle ok I was thinking of WPF which uses the Dispatcher-mechanism. – Davio May 15 '12 at 14:14
  • 1
    A similar problem (and possible solution) has been posted here: http://stackoverflow.com/questions/3467816/problem-with-invoke-to-parallelize-foreach – Davio May 15 '12 at 14:18
  • @ThorstenDittmar: Not `Control.BeginInvoke` it's confusingly different to other uses of `Begin/EndInvoke` – Nick May 15 '12 at 14:29
  • Invoke blocks the UI thread; that means if something else is trying to do something in the UI thread it can't because it's blocked (i.e. deadlock). – Peter Ritchie May 15 '12 at 15:00

3 Answers3

7

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.

svick
  • 236,525
  • 50
  • 385
  • 514
  • when you say in the 1st paragraph "including the current one" you are talking about the UI (main) thread? I've to execute MyRepresentation() on the UI thread because I'm doing some calls to the OpenGL's API and the context is created in the UI thread – Michelle May 15 '12 at 15:02
  • 1
    Yes, if you run `ForEach()` from the UI thread, then some of its iterations will run on the UI thread. And if you have to run `new MyRepresentation()` on the UI thread, then why are you even trying to use `Parallel.ForEach()`? There doesn't seem to be anything in the code that *can* be parallelized. – svick May 15 '12 at 15:12
  • @sivck thank you for your help now I've 100% clear what was happening. You're right about the execution of MyRepresentation() using the Parallel.ForEach() but I've to do some intensive calculations inside MyRepresentation and very few lines inside the Invoke to do the calls to the OpenGL's API. But I didn't show it here for simplicity – Michelle May 15 '12 at 15:35
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);
  }));
}

}
JSJ
  • 5,653
  • 3
  • 25
  • 32
  • So Control.Invoke() can create a deadlock? – Michelle May 15 '12 at 14:00
  • 2
    Yes, `Control.Invoke` can create a deadlock if the control you're invoking on is waiting for the thread that is calling `Control.Invoke` to finish. There are conditions when `Control.Invoke` can create deadlocks - it's your task to resolve this :-) – Thorsten Dittmar May 15 '12 at 14:13
  • @ThorstenDittmar thank you for your explanation. But this is not the case because the view control is a Third Party one – Michelle May 15 '12 at 14:21
  • @Jodha I've tried using lock like you suggested me and I'm still in the same situation – Michelle May 15 '12 at 14:22
  • 1
    The `lock` wouldn't help here. You would still get the same deadlock. – svick May 15 '12 at 14:35
-1

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.

geoff
  • 2,251
  • 1
  • 19
  • 34