Book Image

Multithreading with C# Cookbook, Second Edition - Second Edition

By : Evgenii Agafonov
Book Image

Multithreading with C# Cookbook, Second Edition - Second Edition

By: Evgenii Agafonov

Overview of this book

Multi-core processors are synonymous with computing speed and power in today’s world, which is why multithreading has become a key concern for C# developers. Multithreaded code helps you create effective, scalable, and responsive applications. This is an easy-to-follow guide that will show you difficult programming problems in context. You will learn how to solve them with practical, hands-on, recipes. With these recipes, you’ll be able to start creating your own scalable and reliable multithreaded applications. Starting from learning what a thread is, we guide you through the basics and then move on to more advanced concepts such as task parallel libraries, C# asynchronous functions, and much more. Rewritten to the latest C# specification, C# 6, and updated with new and modern recipes to help you make the most of the hardware you have available, this book will help you push the boundaries of what you thought possible in C#.
Table of Contents (18 chapters)
Multithreading with C# Cookbook Second Edition
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Locking with a Monitor construct


This recipe illustrates another common multithreaded error called a deadlock. Since a deadlock will cause a program to stop working, the first piece in this example is a new Monitor construct that allows us to avoid a deadlock. Then, the previously described lock keyword is used to get a deadlock.

Getting ready

To work through this recipe, you will need Visual Studio 2015. There are no other prerequisites. The source code for this recipe can be found at BookSamples\Chapter1\Recipe10.

How to do it...

To understand the multithreaded error deadlock, perform the following steps:

  1. Start Visual Studio 2015. Create a new C# console application project.

  2. In the Program.cs file, add the following using directives:

    using System;
    using System.Threading;
    using static System.Console;
    using static System.Threading.Thread;
  3. Add the following code snippet below the Main method:

    static void LockTooMuch(object lock1, object lock2)
    {
      lock (lock1)
      {
        Sleep(1000);
        lock (lock2);
      }
    }
  4. Add the following code snippet inside the Main method:

    object lock1 = new object();
    object lock2 = new object();
    
    new Thread(() => LockTooMuch(lock1, lock2)).Start();
    
    lock (lock2)
    {
      Thread.Sleep(1000);
      WriteLine("Monitor.TryEnter allows not to get stuck, returning false after a specified timeout is elapsed");
      if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))
      {
        WriteLine("Acquired a protected resource succesfully");
      }
      else
      {
        WriteLine("Timeout acquiring a resource!");
      }
    }
    
    new Thread(() => LockTooMuch(lock1, lock2)).Start();
    
    WriteLine("----------------------------------");
    lock (lock2)
    {
      WriteLine("This will be a deadlock!");
      Sleep(1000);
      lock (lock1)
      {
        WriteLine("Acquired a protected resource succesfully");
      }
    }
  5. Run the program.

How it works...

Let's start with the LockTooMuch method. In this method, we just lock the first object, wait for a second, and then lock the second object. Then, we start this method in another thread and try to lock the second object and then the first object from the main thread.

If we use the lock keyword like in the second part of this demo, there will be a deadlock. The first thread holds a lock on the lock1 object and waits while the lock2 object gets free; the main thread holds a lock on the lock2 object and waits for the lock1 object to become free, which will never happen in this situation.

Actually, the lock keyword is syntactic sugar for the Monitor class usage. If we were to disassemble code with lock, we would see that it turns into the following code snippet:

bool acquiredLock = false;
try
{
  Monitor.Enter(lockObject, ref acquiredLock);

// Code that accesses resources that are protected by the lock.

}
finally
{
  if (acquiredLock)
  {
    Monitor.Exit(lockObject);
  }
}

Therefore, we can use the Monitor class directly; it has the TryEnter method, which accepts a timeout parameter and returns false if this timeout parameter expires before we can acquire the resource protected by lock.