ARTICLE

Thread Locking in C#

Posted by Akshay Teotia Articles | Multithreading in C# December 03, 2011
Exclusive locking in threading ensures that one thread does not enter a critical section while another thread is in the critical section of code.
Reader Level:

Thread Locking

Exclusive locking in threading ensures that one thread does not enter a critical section while another thread is in the critical section of code. If another thread attempts to enter a locked code, it will wait (block) until the object is released. To achieve this functionality we have to main exclusive locking constructs are

  • Mutex
  • Lock

The lock construct is faster and more convenient. Mutex, though, has a place in that its lock can span applications in different processes on the computer.

The lock statement takes the following form:

lock(expression) statement_block

Where

expression : Specifies the object that you want to lock on. expression must be a reference type.
statement_block :The statements of the critical section. A critical section is a piece of code that accesses a shared resource (data structure or device) but the condition is that only one thread can enter in this section in a time.

In the following example we implement threads and lock. As long as the lock statement is present, the statement block is a critical section and Salary will never become a negative number.

using System;
using System.Threading;
using System.Diagnostics;
namespace Threadlocking
{
class
Department
    {
        private Object thisLock = new Object();
        int salary;
        Random r = new Random();
        public Department(int initial)
        {
            salary = initial;
        }
        int Withdraw(int amount)
        {
            // This condition never is true unless the lock statement
            // is commented out.
            if (salary < 0)
            {
                throw new Exception("Negative Balance");
            }
            // Comment out the next line to see the effect of leaving out
            // the lock keyword.
            lock (thisLock)
            {
                if (salary >= amount)
                {
                    Console.WriteLine("salary before Withdrawal :  " + salary);
                    Console.WriteLine("Amount to Withdraw        : -" + amount);
                    salary = salary - amount;
                    Console.WriteLine("salary after Withdrawal  :  " + salary);
                    return amount;
                }
                else
                {
                    return 0; // transaction rejected
                }
            }
        }
        public void DoTransactions()
        {
            for (int i = 0; i < 100; i++)
            {

                Withdraw(r.Next(1, 100));
            }
        }
    }
class Process
    {
        static void Main()
        {
            Thread[] threads = new Thread[10];
            Department dep = new Department(1000); 
           for (int i = 0; i < 10; i++)
            {
               Thread t = new Thread(new ThreadStart(dep.DoTransactions));
                threads[i] = t;
            }
            for (int i = 0; i < 10; i++)
            {
                threads[i].Start();
            }
            Console.Read();
        }
    }
}

Output :

lock.gif

In general, we avoid locking because lock (this) is a problem if the instance can be accessed publicly.

Nested Locking

A thread can repeatedly lock the same object in a nested (reentrant) fashion:

lock (locker)
lock (locker)
lock (locker)
{
// Do something...
}

In the nested locking, the object is unlocked only when the outermost lock statement has exited. It is useful when one method calls another within a lock:

using System;
using System.Threading;
using System.Diagnostics;
namespace nestedlocking
{
class Program
    {
        static object x = new object();
        static void AnotherMethod()
        {
            lock (x) { Console.WriteLine("Another method"); }
        }
        static void Main()
        {
            lock (x)
            {
                AnotherMethod();
                // We still have the lock - because locks are reentrant.
            }
            Console.Read();
        }
    }
}

Output:

nestloc.gif

The problem with threading

Lock-based resource protection and thread/process synchronization have many disadvantages:

  • They cause blocking, which means some threads/processes have to wait until a lock (or a whole set of locks) is released.

  • Lock handling adds overhead for each access to a resource, even when the chances for collision are very rare. (However, any chance for such collisions is a race condition).

  • Lock contention limits scalability and adds complexity.

  • Priority Inversion High priority threads/processes cannot proceed, if a low priority thread/process is holding the common lock.

  • Convoying. All other threads have to wait, if a thread holding a lock is descheduled due to a time-slice interrupt or page fault.

  • Hard to debug: Bugs associated with locks are time dependent. They are extremely hard to replicate.

Login to add your contents and source code to this article
post comment
     
COMMENT USING
PREMIUM SPONSORS
DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and add new content to existing PDF documents from within your applications.
Join a Chapter
SPONSORED BY
  • PDF reports have never been easier to create. With our included WYSIWYG Designer, you can layout your reports, set up your data source and let DynamicPDF ReportWriter do the rest.
Get Career Advice from Experts