Using a mutual exclusion lock

Locking is essential in parallel programs. It restricts code from being executed by more than one thread at the same time. Exclusive locking is used to ensure that only one thread can enter a particular section of code at a time.

The simplest way to use synchronization in c# is with the lock keyword. The lock keyword works by marking a block of code as a critical section by obtaining a mutual exclusion lock for an object running a statement and then releasing the lock.

In this recipe, we are going to create a class that represents a bank account. An object of this class will be shared by a couple of parallel tasks that will be making a series of withdrawals for random amounts. The critical section of code in the Withdraw method that updates the balance of the shared account object will be protected by a lock statement.

How to do it…

Let's go to Visual Studio 2012 and take a look at the following steps on how to use mutual exclusion locks:

  1. Start a new project using the C# Console Application project template and assign LockExample as the Solution name.
  2. Add a new class to the project and name the class Account.cs.
  3. Add the following code snippet using directives to the top of your Account class:
    using System.Text;
    using System.Threading.Tasks;
  4. Add a private field of type double to the Account class to store the balance of the account and a private object that will be used for locking.
    private double _balance;
    private object _locker = new object();
  5. Next let's add a constructor to the Account class. This constructor should accept a parameter of type double and should initialize the balance field.
    public Account(double initialBalance)
    {
      _balance = initialBalance;
    }
    
  6. Now let's create a Withdraw method for the account. If the account has a negative balance, the Withdraw method should throw an error. Otherwise, the Withdraw method should obtain a mutual exclusion lock on the Account object and deduct the requested amount from the balance.
    public double Withdraw(double amount)
    {
    
      if (_balance < 0)
      throw new Exception("Account has a negative balance.");
      }
    
      lock (_locker)
      {
        if (_balance >= amount)
        {
          Console.WriteLine("Starting balance :  " + _balance);
          Console.WriteLine("Withdraw  amount : -" + amount);
          _balance = _balance - amount;
          Console.WriteLine("Current balance :  " + _balance);
          return amount;
        }
        else
        {
          return 0;
        }
      }
    }
  7. Now let's go back to our Program class. Make sure to add the following code snippet using directives that are at the top of the Program class:
    using System;
    using System.Threading.Tasks;
  8. Create a static DoTransactions method for the Program class. The DoTransactions method should loop ten times doing a withdrawal of a random amount.
    static void DoTransactions(Account account)
    {
      Random r = new Random();
      for (int i = 0; i < 10; i++)
      {
        account.Withdraw((double)r.Next(1, 100));
      }
    }
  9. Finally, in the Main method of the Program class, let's create a shared account object and two tasks that will concurrently execute the withdrawals. Finish up by waiting for the user input before exiting.
    static void Main(string[] args)
    {
      Account account = new Account(1000);
      Task task1 = Task.Factory.StartNew(() => DoTransactions(account));
      Task task2 = Task.Factory.StartNew(() => DoTransactions(account));
      Console.ReadLine();
    }
  10. In Visual Studio 2012, press F5 to run the project. You should see output similar to the following screenshot:
    How to do it…

How it works…

The lock keyword is a c# language shortcut for using the System.Threading.Monitor class. Basically, the lock keyword ensures that threads cannot enter a critical section of code while another thread is in the critical section; the following is the code contained in the scope of the lock statement:

Tip

A critical section is simply a piece of code that accesses a shared resource that must not be concurrently accessed by more than one thread.

lock (this)
{
  //This is the critical section
}

If a thread tries to enter a locked section of code, it will block and wait until the locked object is released. The lock will be released when the locking thread exits the scope of the lock. The lock keyword calls System.Threading.Monitor.Enter at the start of the scope and System.Threading.Monitor.Exit at the end of the scope.

Notice that we created a private lockable object to lock on instead of locking on the instance of the Account class. This is the best practice. In general, you should avoid locking on a public type or on instances of objects that are beyond your code's control. If another programmer locks your class to synchronize their data, a deadlock can occur. A deadlock is a situation in which two or more competing threads are waiting for each other to finish their work and release a lock, and thus neither one ever does. Note also that locks can only be obtained on reference types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.24.60