What locks does multithreading have?

Mutex and spin lock

The purpose of locking is to ensure that shared resources are accessed by only one thread at any time, so that the problem of shared data confusion caused by multi-threading can be avoided

When a thread has already locked, other threads will fail to lock. Mutex locks and spin locks have different processing methods for locking failures:

  1. ​ After the mutex lock fails, the thread will release the CPU to other threads;
  2. ​ After the spin lock fails to lock, the thread will wait busy until it gets the lock;

A mutex is a kind of "exclusive lock". For example, when thread A successfully locks, the mutex has been exclusively occupied by thread A at this time. As long as thread A does not release the lock in hand, thread B will fail to lock, so It will release the CPU to other threads. Since thread B releases the CPU, the code that locks thread B will naturally be blocked. The phenomenon of blocking due to mutex locking failure is implemented by the operating system kernel. When the lock fails, the kernel will put the thread into a "sleep" state. After the lock is released, the kernel will wake up the thread at an appropriate time . When the thread successfully acquires the lock, it can continue to execute. As shown below:
insert image description here

Although the mutex simplifies the difficulty of using locks, there is a certain performance overhead, that is, the cost of two thread context switches : when the thread fails to lock, the kernel will set the state of the thread from "running" to " Then, when the lock is released, the thread in the previous "sleep" state will become "ready" state, and then the kernel will switch the CPU to the thread at an appropriate time run.

What is thread context switching? When two threads belong to the same process, because virtual memory is shared, resources such as virtual memory remain unchanged when switching, and only data that is not shared, such as private data and registers of threads, need to be switched.

The spin lock is a CAS (Compare And Swap) function provided by the CPU. The lock and unlock operations are completed in the user mode, and the thread context switch will not be actively generated. Therefore, compared with the mutex, it will be faster and the overhead will be lower .

The general locking process consists of two steps:

  1. The first step is to check the status of the lock. If the lock is free, execute the second step;
  2. The second step is to set the lock to be held by the current thread;

The CAS function combines these two steps into one hardware-level instruction to form an atomic instruction, which ensures that the two steps are inseparable. Either the two steps are executed at one time, or neither step is executed.

For example, if the lock is the variable lock, the integer 0 indicates that the lock is idle, and the integer pid indicates the thread ID, then CAS(lock, 0, pid) indicates the locking operation of the spin lock, and CAS(lock, pid, 0) then Indicates an unlock operation.

When using a spin lock, when multiple threads compete for a lock, the thread that fails to lock will "busily wait" until it gets the lock. The "busy waiting" here can be realized by waiting in a while loop , but it is better to use the PAUSE instruction provided by the CPU to realize "busy waiting", because it can reduce the power consumption during the loop waiting.

The spin lock is the simplest kind of lock, which keeps spinning and uses CPU cycles until the lock is available. It should be noted that on a single-core CPU, a preemptive scheduler is required . Otherwise, spinlocks are useless on a single CPU, because a spinning thread never relinquishes the CPU.

The spin lock overhead is low, and thread switching is generally not actively generated in a multi-core system. However, if the locked code executes for a long time, the spinning thread will occupy CPU resources for a long time, so the spin time and locked The execution time of the code is "proportional", we need to know this clearly.

The use level of spin lock and mutex is similar, but the implementation level is completely different: when the lock fails, the mutex uses "thread switching" to deal with it, and the spin lock uses "busy waiting" to deal with it.

read-write lock

The read-write lock is composed of [read lock] and [write lock]. Only read the shared resource 1 and use the [read lock] to lock it. If you want to modify the shared resource, use the [write lock] to lock it.

The working principle of read-write lock is:

  1. ​ When [write lock] is not occupied by threads, [read lock] can be held concurrently by multiple threads
  2. ​ When the [Write Lock] is occupied by a thread, the operation of the read thread to acquire the read lock will be blocked, and the operation of other write threads to acquire the write lock will also be blocked.

Optimistic and pessimistic locking

Pessimistic locks are more pessimistic. It believes that the probability of multiple threads modifying shared resources at the same time is relatively high, so conflicts are prone to occur. Therefore, before accessing shared resources, lock them first.

On the contrary, if the probability of multiple threads modifying shared resources at the same time is relatively low, optimistic locking can be used. Optimistic locking works more optimistically. It assumes that the probability of conflict is very low. Its working method is: first modify the shared resource, and then verify whether there is any conflict during this period. If no other thread is modifying the resource, the operation is completed. If If it is found that other threads have modified this resource, the operation is abandoned.

reentrant lock

Reentrant lock refers to the unit of thread. When a thread acquires the object lock, the thread can acquire the lock on the object again, while other threads cannot. The principle of a reentrant lock is to maintain a thread ID inside the lock to identify which thread the lock is currently held by, and then associate a counter . At the beginning, the counter is 0 (that is, the lock is not occupied). When a thread acquires the lock, the value of the counter will be +1. At this time, when other threads acquire the lock again, they will find that the lock holder is not themselves but is blocked and hangs. But when the thread that acquired the lock acquires the lock again and finds that the owner of the lock is itself, it will increase the counter value by 1 (into 2, 3, 4, 5...), when the lock is released, the counter -1, when the counter When the value is 0, the thread flag in the lock is reset to null, and the blocked thread will be awakened to compete for the lock .

Guess you like

Origin blog.csdn.net/TABE_/article/details/126538429