Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deadlock when thread uses dispatcher and the main thread is waiting for thread to finish

Can someone please explain why this creates a deadlock, and how to solve it?

        txtLog.AppendText("We are starting the thread" + Environment.NewLine);

        var th = new Thread(() =>
        {

            Application.Current.Dispatcher.Invoke(new Action(() => // causes deadlock
            {
                txtLog.AppendText("We are inside the thread" + Environment.NewLine); // never gets printed
                // compute some result...
            }));


        });

        th.Start();
        th.Join(); // causes deadlock
        // ... retrieve the result computed by the thread

Explanation: I need my secondary thread to compute a result, and to return it to the main thread. But the secondary thread must also write debug informations to the log; and the log is in a wpf window, so the thread needs to be able to use the dispatcher.invoke(). But the moment I do Dispatcher.Invoke, a deadlock occurs, because the main thread is waiting for the secondary thread to finish, because it needs the result.

I need a pattern to solve this. Please help me rewrite this code. (Please write actual code, do not just say "use BeginInvoke"). Thank you.

Also, theoretically, I don't understand one thing: a deadlock can only happen when two threads access two shared resources in different orders. But what are the actual resources in this case? One is the GUI. But what is the other? I can't see it.

And the deadlock is usually solved by imposing the rule that the threads can only lock the resources in a precise order. I've done this already elsewhere. But how can I impose this rule in this case, since I don't understand what the actual resources are?

like image 247
seguso Avatar asked Jun 13 '14 18:06

seguso


2 Answers

Short answer: use BeginInvoke() instead of Invoke(). Long answer change your approach: see the altenative.

Currently your Thread.Join() is causing that main thread get blocked waiting for the termination of secondary thread, but secondary thread is waiting to main thread executes your AppendText action, thus your app is deadlocked.

If you change to BeginInvoke() then your seconday thread will not wait until main thread executes your action. Instead of this, it will queue your invocation and continues. Your main thread will not blocked on Join() because your seconday thread this time ends succesfully. Then, when main thread completes this method will be free to process the queued invocation to AppendText

Alternative:

void DoSomehtingCool()
{
    var factory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
    factory.StartNew(() =>
    {
        var result = await IntensiveComputing();
        txtLog.AppendText("Result of the computing: " + result);
    });
}

async Task<double> IntensiveComputing()
{
    Thread.Sleep(5000);
    return 20;
}
like image 153
Jairo Andres Velasco Romero Avatar answered Sep 30 '22 14:09

Jairo Andres Velasco Romero


This deadlock happens because the UI thread is waiting for the background thread to finish, and the background thread is waiting for the UI thread to become free.

The best solution is to use async:

var result = await Task.Run(() => { 
    ...
    await Dispatcher.InvokeAsync(() => ...);
    ...
    return ...;
});
like image 25
SLaks Avatar answered Sep 30 '22 14:09

SLaks