Deep understanding of mutex lock implementation

Source: Internet
Author: User

In actual software programming, resource contention is often encountered, for example, the following example:

Class counter {PRIVATE: int value; public: Counter (INT c) {value = C;} int getandincrement () {int temp = value; // enter the hazardous area value = temp + 1; // return value before leaving the hazardous area ;}}

This implementation works normally in a single-threaded system, but may cause errors in a multi-threaded system. For example, there are two threads and the initial state value is 0. When the first thread runs 9th rows, temp = 0. Suddenly an interruption occurred, and the second thread was switched to run. After the second thread was run, 9th rows were also temp = 0, and then 10th rows were assigned value = 1. Then return to the first thread and continue to run the 10th rows to overwrite the value. The result value is 1. The correct case is value = 2.
Why is this happening? In this case, two threads compete for a resource value at the same time. To avoid the above situation, we can place the two rows in the critical section: the code segment that can be executed by only one thread at a certain time point. To achieve mutual exclusion. Add the counter class for mutex access to the critical section:

Class counter {PRIVATE: int value; lock; public: Counter (INT c) {value = C;} int getandincrement () {lock. lock (); // get the lock int temp = value; // enter the critical section value = temp + 1; // exit the lock. unlock (); // release the lock return value ;}}

In order to use the lock domain in the program to ensure the mutex feature of the object, lock () and unlock () must be called symmetric (). The following conditions must be met:
1. A critical section is associated with a lock pair.
2. Call lock () before the thread enters the critical section ().
3. Call unlock () after the thread leaves the critical section ().
The programming framework is as follows:
Lock ()
Critical Section
Unlock ()
A metaphor is not very rational. It is like having a warehouse resource, but there are many people who want to do something in the warehouse. At this time, the warehouse only needs a lock (too much lock is a waste of money ^_^), and the lock on the Initial State Warehouse is opened. Before everyone goes in, lock the lock (to prevent others from coming in), and then tamper with the lock in the warehouse. When they leave, they can open the lock in the warehouse so that others can come in.
The next step is how to implement the mutex lock?That is, the lock () and unlock () methods.

Class lock {public: Virtual void lock () = 0; // virtual void unlock () = 0 before entering the critical section; // after leaving the critical section}

The mutex lock must meet three conditions:
Mutual ExclusionThe critical zones of different threads do not overlap.
No DeadlockIf a thread is trying to obtain a lock, it will always succeed. If thread a calls lock () but cannot obtain the lock, other threads Must be executing the critical section infinitely.
No hungerEvery thread trying to get the lock will eventually succeed.
First, let's look at the mutual exclusion of the two threads. First, let's look at the two shortcomings (if you don't see the subsequent analysis, you can also know where the problem is, it's even worse), but the very interesting lock algorithm:
Lockone class
This class has a flag array. To continue, this flag is equivalent to a flag. The lockone class follows this Protocol:
1. If the thread wants to enter the critical section, first raise the flag (flag corresponding position 1) to indicate interest. Then, when the other party's flag falls, it can enter the critical section.
2. If the thread leaves the critical section, lower its flag.

  class LockOne: public Lock  {       private:         bool flag[2];       public:         void lock()         {            int i = ThreadID.get();            int j = 1-i;            flag[i] = true;            while(flag[j]);         }         void unlock()         {            int i = ThreadID.get();            flag[i] = false;         } }

Lockone-class protocols seem simple, but there is a problem: when both threads raise the flag, then, wait for the other party's flag to fall down and the deadlock will occur (both threads are waiting for the other party's flag to fall down until the sky reaches the ground :))

Locktwo class
Observe the problems of the lockone class, that is, when two threads raise the flag at the same time, we need to have a thread compromise, so we need to specify a victim, so the locktwo class is born.

Class locktwo: Public lock {PRIVATE: int victim; public: void lock () {int I = threadid. get (); victim = I; // Let others first, temporarily sacrifice yourself while (victim = I);} void unlock () {]}

When two threads compete, there is always a victim (the thread assigned a value to Victim Later), so the deadlock can be avoided. However, when there is no competition, there will be a cup of cake. If only one thread wants to enter the critical section, the victim will always be himself, waiting for others to replace himself.

Perterson lock
The above two classes show that the lockone class is suitable for non-competitive scenarios, and the locktwo class is suitable for competitive scenarios. Then we can combine the lockone class with the locktwo class to construct a good lock algorithm. This algorithm is undoubtedly the most concise and perfect dual-thread mutex algorithm. It is named "Peterson algorithm" according to the inventor's name ".

class Peterson: public Lock{   private:     bool flag[2];     int victim;   public:     void lock()     {        int i = ThreadID.get();        int j = 1-i;        flag[i] = true;        victim = i;        while(flag[j] && victim==i);      }      void unlock()      {         int i = ThreadID.get();         flag[i] = false;      }}

Perterson locks are mutually exclusive. The reverse verification method is used to show that if both threads want to enter the critical section, but both threads have successfully entered the critical section. Because both threads want to enter, it means that the corresponding flags are 1, and then because both can lock () successfully, it means that victim is not himself. This is in conflict with victim.

However, in practice, there cannot be only two threads. Next we need to look at the mutex protocol that supports n threads.

Barkey lock

There is a protocol called bakery lock, which is the simplest and most well-known N-thread lock algorithm. Let's take a look at the situation. The idea is very simple. Let's just give a simple analogy to the interpreter protocol:
1. Each thread raises its own flag and obtains a serial number before entering the critical section. The thread with the smallest serial number in the thread that raises the flag can enter the critical section.
2. When each thread leaves the critical section, drop its own flag.

class Bakery: public Lock{  private:     bool flag[];    Label label[];  public:    Bakery (int n)    {      flag = new bool[n];      label = new Label[n];      for(int i=0; i<n; i++)      {        flag[i] = flase;        label[i]  =  0;      }      void Lock()      {         int i = ThreadID.get();         flag[i] = true;                        label[i] = max(label[0], ..., label[n-1]) +1;         while((exist k!=i)(flag[k] && (label[k],k)<<(label[i], i))       }       void unlock()       {          flag[TheadID.get()] = false;       }     }}

First, the barkey algorithm has no deadlock. Because there must be a minimum serial number label in the waiting thread (similar to all threads that raise the flag. This thread can enter the critical section.
Secondly, the barkey algorithm is first served. Therefore, the thread that comes first has a small label.
Finally, the barkey algorithm is mutually exclusive. If both threads are located in the critical section at the same time, both threads have raised the flag and the label is the smallest, which is a conflict.
It is important to implement a mutex lock for n threads. At least n storage units must be used. If a thread is in the critical section, but the lock status is consistent with the global status of a thread that is not in the critical section or is in the critical section, the status is inconsistent. That is, each thread has two states, and n threads have two ^ n States. A total of N memories are required to record the global states.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.