0

The code below is an example on multi-threading that the prof presented in class. I am new to coding (first course). I have read on multi-threading and using locks. Reading the theory is fun. var fun = Theory.Read(multi-threading); Actually coding threads and locks seems to baffle me.
Trying to understand how the two threads in the code below will behave. From testing the code it looks like lock1 will not release and message2 is not enqueue-ed, but I might be wrong. Looks like there is a synchronization issue. Is this an example of a deadlock? I am also wondering why locks and threads are required if two different queues are used. I am not seeing a shared resource. Is there a way to fix this code to prevent the synchronization issue?

private static object Lock1 = new object(); // Protect MessageQueueOne
private static object Lock2 = new object(); // Protect MessageQueueTwo
private static Queue<string> MessageQueueOne = new Queue<string>();
private static Queue<string> MessageQueueTwo = new Queue<string>();

private static void AddMessages(string message1, string message2)
{
    lock (Lock1)
    {
        // (1) Thread 1 is here...
        MessageQueueOne.Enqueue(message1);

        lock (Lock2)
        {
            MessageQueueTwo.Enqueue(message2);
        }
    }
}

private static void RemoveMessages()
{
    lock (Lock2)
    {
        if (MessageQueueTwo.Count > 0)
        {
            // (2) Thread 2 is here...
            Console.WriteLine(MessageQueueTwo.Dequeue());
        }

        lock (Lock1)
        {
            if (MessageQueueOne.Count > 0)
            {
                Console.WriteLine(MessageQueueOne.Dequeue());
            }
        }
    }
}

private static void Main()
{
    Task taskOne = Task.Run(() =>
    {
        for (int i = 0; i < 100; ++i)
        {
            AddMessages($"Message One: {DateTime.Now}", $"Message Two: {DateTime.UtcNow}");

            Thread.Sleep(25);
        }
    });

    Task taskTwo = Task.Run(() =>
    {
        for (int i = 0; i < 100; ++i)
        {
            RemoveMessages();

            Thread.Sleep(25);
        }
    });

    taskOne.Wait();

    taskTwo.Wait();

    Console.Write("Tasks are finished");

    Console.ReadKey();
}
ClaudiaR
  • 1
  • 2
  • 1
    You probably slept through half of the class and missed part where "classical deadlocks" where described... – Alexei Levenkov Apr 17 '17 at 05:56
  • Your question is very broad, and best addressed to your instructor, not Stack Overflow. Short version: deadlock happens with two concurrently executing sections of code attempt to acquire two different locks, in different orders, and each section successfully acquires the first lock they try to acquire, before either section is able to acquire the next lock they try to acquire. Classic fix is to make sure all code sections always acquire all locks in the same order. – Peter Duniho Apr 17 '17 at 05:59
  • 1
    Most of your questions can probably be answered simply by "the code is that way because your instructor desired to provide an example of deadlock", nothing more. – Peter Duniho Apr 17 '17 at 06:00
  • @Alexei Thank you for taking the time to review the code and post an answer. Are you saying that the code is an example of "classical deadlock"? if (awake == true & instructorAbsent == true ) {Elaborate; WalkMeThough; } – ClaudiaR Apr 17 '17 at 06:39
  • @Peter Duniho Thank you Peter. It makes sense. I will take your suggestion and try to fix the code to run without a deadlock. Wish instructor or tutoring center on campus were available. – ClaudiaR Apr 17 '17 at 06:55

1 Answers1

0

The code in the post is classical example of deadlock and expected to deadlock most of the time. See more links in Wikipedia article on deadlocks.

What leads to deadlock: one thread locks "lock1" and waits for "lock2", the other thread at the same time holds lock on "lock2" and will release it after acquiring "lock1" which will never be release by waiting thread.

Standard solutions

  • listen to your class to know the answer
  • read existing examples
  • if above fails - one option is to acquire resources in fixed order (i.e. if need to lock on more than one resource get "lock1" first, than "lock2" and so on) for all thread (Would you explain lock ordering?).
Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Just as I thought from the theory I researched. Appreciate you explaining it with the code. Solutions: Done(meager as it is). Done(extensively. needle in the haystack of the www). and Never heard of 'lock ordering' and apparently 'unlocking.' Thank you for mentioning it. Continuing my research. It seems you know about locks and synchronization. Any awesome book / author you recommend on the topic? – ClaudiaR Apr 17 '17 at 07:29
  • 1
    @ClaudiaR Check [this answer](http://stackoverflow.com/q/5040545/213550) and [this book](https://www.safaribooksonline.com/library/view/professional-parallel-programming/9780470495995/), this can be a good start – VMAtm Apr 26 '17 at 00:13