Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CLR via C# 4th Ed. - Confused about waiting for Task deadlock

Jeffrey Richter pointed out in his book 'CLR via C#' the example of a possible deadlock I don't understand (page 702, bordered paragraph).

The example is a thread that runs Task and call Wait() for this Task. If the Task is not started it should possible that the Wait() call is not blocking, instead it's running the not started Task. If a lock is entered before the Wait() call and the Task also try to enter this lock can result in a deadlock.

But the locks are entered in the same thread, should this end up in a deadlock scenario?

The following code produce the expected output.

class Program
{
    static object lockObj = new object();

    static void Main(string[] args)
    {
        Task.Run(() =>
        {
            Console.WriteLine("Program starts running on thread {0}",
                Thread.CurrentThread.ManagedThreadId);
            var taskToRun = new Task(() =>
            {
                lock (lockObj)
                {
                    for (int i = 0; i < 10; i++)
                        Console.WriteLine("{0} from Thread {1}", 
                            i, Thread.CurrentThread.ManagedThreadId);
                }
            });

            taskToRun.Start();
            lock (lockObj)
            {
                taskToRun.Wait();
            }

        }).Wait() ;
    }
}

/* Console output
Program starts running on thread 3
0 from Thread 3
1 from Thread 3
2 from Thread 3
3 from Thread 3
4 from Thread 3
5 from Thread 3
6 from Thread 3
7 from Thread 3
8 from Thread 3
9 from Thread 3
*/

No deadlock occured.

J. Richter wrote in his book "CLR via C#" 4th Edition on page 702:

When a thread calls the Wait method, the system checks if the Task that the thread is waiting for has started executing. If it has, then the thread calling Wait will block until the Task has completed running. But if the Task has not started executing yet, then the system may (depending on the TaskScheduler) execute the Trask by using the thread that called Wait. If this happens, then the thread calling Wait does not block; it executes the Task and returns immediatlely. This is good in that no thread has blocked, thereby reducing resource usage (by not creating a thread to replace the blocked thread) while improving performance (no time is spet to create a thread an there is no contexte switcing). But it can also be bad if, for example, thre thread has taken a thread synchronization lock before calling Wait and thren the Task tries to take the same lock, resulting in a deadlocked thread!

If I'm understand the paragraph correctly, the code above has to end in a deadlock!?

like image 619
embee Avatar asked Mar 02 '14 19:03

embee


1 Answers

You're taking my usage of the word "lock" too literally. The C# "lock" statement (which my book discourages the use of), internally leverages Monitor.Enter/Exit. The Monitor lock is a lock that supports thread ownership & recursion. Therefore, a single thread can acquire this kind of lock multiple times successfully. But, if you use a different kind of lock, like a Semaphore(Slim), an AutoResetEvent(Slim) or a ReaderWriterLockSlim (without recursion), then when a single thread tries to acquire any of these locks multiple times, deadlock occurs.

like image 122
Jeffrey Richter Avatar answered Oct 12 '22 00:10

Jeffrey Richter