RotatingWheel
RotatingWheel

Reputation: 1041

Resolving locking deadlock with Thread.Sleep

If I comment or pass 0 in Thread.Sleep(0) method then there is no deadlock. In other cases there is a deadlock. uptask is executed by a thread from the thread poll and that takes some time. In the mean time, the main thread acquires lockB, lockA and prints the string and releases the locks. After that uptask starts running and it sees that lockA and lockB are free. So in this case there is no deadlock. But if I sleep the main thread in the mean time uptask advances and sees that lockB is locked and a deadlock happens. Can anybody explain better or verify if this is the reason?

class MyAppClass
{
    public static void Main()
    {
        object lockA = new object();
        object lockB = new object();
        var uptask = Task.Run(() =>
        {
            lock (lockA)
            {
                lock (lockB)
                {
                    Console.WriteLine("A outer and B inner");
                }
            }
        });
        lock (lockB)
        {
            //Uncomment the following statement or sleep with 0 ms and see  that there is no deadlock.
            //But sleep with 1 or more lead to deadlock. Reason?
            Thread.Sleep(1);
            lock (lockA)
            {
                Console.WriteLine("B outer and A inner");
            }
        }
        uptask.Wait();
        Console.ReadKey();
    }
}

Upvotes: 2

Views: 2714

Answers (4)

EugeneTheGeek
EugeneTheGeek

Reputation: 1

Martin Liversage answered this question concisely.

To paraphrase, the code in your experiment is deadlock prone, even without the Thread.Sleep() statement. Without the Thread.Sleep() statement, the probability window for the deadlock to occur was extremely small and may have taken eons to occur. This is the reason you did not experience it when omitting the Thread.Sleep() statement. By adding any time-consuming logic at line 19 (ie: Thread.Sleep), you expand this window and increase the probability of the deadlock.

Also, this window could expand/decrease by running your code on a different hardware/OS, where task scheduling may be different.

Upvotes: 0

Yacoub Massad
Yacoub Massad

Reputation: 27871

You really cannot depend on Thread.Sleep to prevent a deadlock. It worked in your environment for some time. It might not work all the time and might not work in other environments.

Since you are obtaining the locks in reverse order, then the chance of a deadlock is there.

To prevent the deadlock, make sure you obtain the locks in order (e.g. lockA then lockB in both threads).

My best guess for why this is happening is that if you don't sleep, then the main thread will obtain and release both locks before the other thread (from the thread pool) obtains lockA. Please note that scheduling and running a Task on the thread-pool requires some time. In most cases, it is neglectable. But it your case, it made a difference.

To verify this, add the following line right before uptask.Wait():

Console.WriteLine("main thread is done");

And this line after obtaining lockA from the thread-pool thread:

Console.WriteLine("Thread pool thread: obtained lockA");

In case where there is no deadlock, you will see the first message printed to the console before the thread-pool thread prints it's message to the console.

Upvotes: 5

Martin Liversage
Martin Liversage

Reputation: 106926

You have two threads. The main thread and the thread that executes the task. If the main thread is able to take lockB and the task thread is able to take lockA then you have a deadlock. You should never lock two different resources in different sequence because that can lead to deadlock (but I expect that you already know this based on the abstract nature of your question).

In most cases the task thread will start slightly delayed and then the main thread will get both locks but if you insert a Thread.Sleep(1) between lockA and lockB then the task thread is able to get lockA before the main thread and BAM! you have a deadlock.

However, the Thread.Sleep(1) is not necessary condition for getting a deadlock. If the operating system decides to schedule the task thread in a way that makes it able to get lockA before the main thread you have your deadlock and just because there is no deadlock on your lightning fast machine you may experience deadlocks on other computers that have fewer processing resources.

Here is an illustration to visually explains why the delay increases the likelihood of getting a deadlock:

Deadlock visualization

Upvotes: 3

Todd Sprang
Todd Sprang

Reputation: 2919

From https://msdn.microsoft.com/en-us/library/d00bd51t(v=vs.110).aspx, "millisecondsTimeout" Type: System.Int32 The number of milliseconds for which the thread is suspended. If the value of the millisecondsTimeout argument is zero, the thread relinquishes the remainder of its time slice to any thread of equal priority that is ready to run. If there are no other threads of equal priority that are ready to run, execution of the current thread is not suspended."

Upvotes: 1

Related Questions