digital_fate
digital_fate

Reputation: 597

Thread.Join() causing deadlock

I have three threads in total. The first is the main UI thread, which starts a System.Threading.Thread (ExperimentThread), which in turn starts a BackgroundWorker (WorkerThread).

MainThread and WorkerThread both access a shared resource. I synchronise access to this resource with the following object:

private static readonly Object LockObject = new Object();

which I use as follows in the main loop of each thread:

lock (LockObject)
{
    // Do something with shared resource here.
}

A cut-down version of ExperimentThread is as follows:

public void RunExperiment
{
    while (!bStopThread)
    {

        lock (LockObject)
        {
            // Do something with shared resource here.
        }

        if (bStopThread)
        {
            break;
        }
        else
        {
            Application.DoEvents();
            Thread.Sleep(250);
        }
    }
}

And for completeness here is the DoWork method of WorkerThread:

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker Worker = sender as BackgroundWorker;

    for (int X = 0; X < 200; X++)
    {
        if (Worker.CancellationPending)
        {
            e.Cancel = true;
            return;
        }

        lock (LockObject)
        {
            // Do something with shared resource here.
        }
    }
}

This seems to work fine when both threads are running freely.

At some point the UI thread will terminate the ExperimentThread by setting one of its boolean fields to true and then wait for it to end, as follows:

if (ExperimentThread.IsAlive)
{
    ExperimentThread.StopThread = true;
    ExperimentThread.Join();    // this line seems to cause the deadlock?
}

As soon as Join() is called, a deadlock occurs on the shared resource being accessed by ExperimentThread and WorkerThread, and my application hangs indefinitely. This happens maybe 9 out of 10 times.

If I remove ExperimentThread.Join() from the code snippet above, the deadlock never occurs, and ExperimentThread appears to terminate gracefully (it then goes on to terminate WorkerThread by calling CancelAsync()).

Any ideas what could be the problem here?

(P.S. I've been using Console.WriteLine() to determine when locks are taken and released, which is what has lead me to believe there's a deadlock. Is there a better to determine this, I could be wrong?)

Upvotes: 1

Views: 4222

Answers (1)

Reed Copsey
Reed Copsey

Reputation: 564681

Is there a better to determine this, I could be wrong?

A better way to check this is to use something like the Concurrency Visualizer available in higher level SKUs of Visual Studio. It will allow you to see exactly what has locked each thread, and what handles threads are waiting on, etc.

As for the exact reason you are getting a deadlock - there isn't enough code to determine this, but common issues are:

  1. ExperimentThread and the main thread (with the Join() call) are both locking on the same object - ie: within a lock(LockObject) statement.
  2. ExperimentThread is using Control.Invoke to marshal a call back onto the UI thread. Since the UI thread is blocked (waiting on the Join()), it can never process messages, which will prevent ExperimentThread from completing.

That being said, in general, I would recommend using Task or Task<T> instead of a new Thread if you're using .NET 4 or higher. Task provides a much nicer API for working with threads, including allowing continuations instead of blocking. C# 5 extends this to even allow you to asynchronously wait for the task to complete.

Upvotes: 2

Related Questions