Tuesday, April 12, 2016

Advanced Programming: An easy way to manage critical zones by locking


So, sometimes people might ask what is the critical zone, or why shall we manage it?
Well, on multi-thread apps like web applications, it is possible that a thread leaves a method and go to sleep and another thread change something that has an impact on those values.
The simplest example that I can come up is an "IncreaseAndSave" method like this:

Code:
void IncreaseAndSave()
{
     var value= ReadValueFromDB();
     Value++;
     // The thread1 goes to sleep
      SaveValueInDB(Value);
}

Simple scenario:
Lets say we have a value "10" in our DB and 2 threads that call our method almost at the same time. The first one will try to run the code, it reads the value from DB and increase it so Value will be "11" in our memory. Then the thread will go to sleep and thread2 will run the process. It will read value "10" again from DB since T1 didn't save it and will increase it to "11". Now both threads will call save method and we will end up with "11" instead of "12".
So the zone that we call a critical zone is from reading to saving Data in the DB.


Simplest Solution
Well there is a reserved word in C# (and almost all other programming languages) for lock. The functionality is very simple. it is like a safe box. When you lock it, no one else can enter or lock it until you leave the safe box and then someone else can take over.
The only important note is that you have to lock the same object :D (safe box)
Code:
void IncreaseAndSave()
{
     lock(_object)
     {
      var value= ReadValueFromDB();
      Value++;
      // The thread1 goes to sleep
       SaveValueInDB(Value);
      }
}

Make it a bit more complex
So, we saw the problem when 2 threads want to access a critical section in a method but what if our critical zone is inside 2 different methods?
For instance, if we have a decrease method, one thread may decrease the value in DB while the other one still didn't save its value.
Of course the solution is to lock both stuff with the single object (the same safe-box)

Make even more Complex
We saw the code for locking some part of codes, but the drawback of that code is that the lock area cannot be accessed by multiple threads even though they might not have an impact on each other.

For instance, take updated function below as an example. The thread thr1 will try to increase the value for key =1 so it will lock the process and goes to sleep, then thr2 will try to increase the value for key=2 but since the area is already locked by thr2, it has to wait.
It is pretty clear that in a simple web application, there will be a long queue for just saving stuff and etc, and it will be slow like if it is running on a computer with only 1 CPU and 1 thread.
So what can we do to make it efficient?

Code:
void IncreaseAndSave(int key)
{
  lock(object)
     {
       var value= ReadValueForKeyFromDB(key);
       Value++;
       // The thread1 goes to sleep
       SaveValueInDB(key,Value);
      }
}


Solution
We need a class to return the lock for each key. The idea is to have a bank of  safe boxes instead of only 1. But we have to pay attention that the bank is the one who is responsible for finding the mutual interest. In the other word, on runtime the thread will ask the LockContainer(banker) about the safe-box with special key and then it will try to lock the safe-box (lock).
It can simply be done by code below:

Code:
  public class LockContainer
    {
        private BlockingCollection<object> lockItems { get; set; }

        private object LocalLockItem { get; set; }


        public LockContainer()
        {
            lockItems = new BlockingCollection<object>();
            LocalLockItem = new object();
        }

        public object GetLockItem(string str)
        {
            lock (LocalLockItem)
            {
                if (!lockItems.Any(li => (string)li == str))
                {
                    lockItems.Add(str);
                }
                return lockItems.First(li => (string)li == str);
            }
        }
    }

* Implementing the process required a list, but at the same time, adding items to list and reading them would make another critical zone, but fortunately, .net has a thread safe list called BlockingCollection which you can use with multiple threads.

How to use the code
You will need to create the container somewhere. If your critical zone is only inside a class, use it as a property of that class and make it static.
If you need to lock different areas in different classes for a key, implement a singleton design pattern (which can be found in here) and return the lock container. Then you can use it easily:
Code:
  LockContainer locks = new LockContainer();
-------------------

void IncreaseAndSave(int key)
{
  lock(locks.GetLockItem(key))
     {
       var value= ReadValueForKeyFromDB(key);
       Value++;
       // The thread1 goes to sleep
       SaveValueInDB(key,Value);
      }
}

  



1 comment: