python - common lock types

1 Lock/RLock ordinary lock (mutual exclusion lock)

        Threads share resources in the same process, and it is easy to compete for resources and generate dirty data . The role of the mutex is to solve the problem of resource contention , allowing a certain part of the resource to be accessed by only one thread at the same time. Note : Try to use mutexes when accessing public resources.

        1.1Lock original lock

         Lock is called the original lock, and no judgment is made before acquiring the lock until the lock is acquired

        1.1.1Lock() instance method

        1 acquire() : Attempts to acquire a lock. Make the thread enter the synchronous blocking state.

        2 release() : Release the lock, the thread must have acquired the lock before use, otherwise an exception will be thrown.

        1.1.2 Small case

import threading
import time
num=0
# def add_num(i):
#     lock.acquire()
#     global num
#     time.sleep(1)
#     num += i
#     print(f"num is {num}")
#     lock.release()

def add_num(i):
    # 使用with语句管理对象 -- 上下文管理器对象
    # 上下文管理器对象  __enter__,  __exit__
    # __enter__方法做一些初始化工作
    # __exit__方法做一些结束收尾工作  释放资源
    with lock:
        global num
        time.sleep(1)
        num += i
        print(f"num is {num}")


t_list = []

# 生成一个锁实例
lock = threading.Lock()
for i in range(10):
    t = threading.Thread(target=add_num, args=(i,))
    t.start()
[t.join() for t in t_list]

print("ending......")

        1.1.3 Deadlock phenomenon

        If you use Lock (original lock) , in the same process, after acquiring the original lock, the lock has not been released and you try to acquire the same original lock, a deadlock phenomenon will occur. However, RLock (reentrant lock) is used . Because it will judge whether it has acquired the lock, it generally does not cause deadlock.

        1.2RLock reentrant lock

        RLock is called a reentrant lock, judge before acquiring the lock, if you have the lock, return immediately

        1.2.1 Small case

import threading
lock1 = threading.Lock()
lock2 = threading.RLock()

# 这种情况会产生死锁
# lock1.acquire()
# print("lock1 acqurie 1")
# lock1.acquire()  # 同一个进程,获取原始锁之后,没有释放又去尝试获取同一个 原始锁,就会产生死锁
# print("lock1 acquire 2")
# lock1.release()
# print("lock1 release 1")
# lock1.release()
# print("lock1 release 2")

lock2.acquire()
print("lock2 acqurie 1")
lock2.acquire()
print("lock2 acquire 2")
lock2.release()
print("lock2 release 1")
lock2.release()
print("lock2 release 2")

        This code well reflects the similarities and differences between Lock and RLock.


2 semaphore (Semaphore)

        Semaphore locks allow up to N threads to execute content at the same time.

        2.1 Construction method

        Semaphore(N) : s = threading.Semaphore(N) creates a semaphore lock object. N is an integer parameter , which means setting the upper limit of threads allowed to execute at the same time.

        2.2 Instance methods

        acquire() : Attempts to acquire a lock, causing the thread to enter a synchronously blocked state.

        release() : Release the lock. The thread must have acquired the lock before using it , otherwise an exception will be thrown.


3 event lock

Event mechanism: A " Flag "         is defined globally , which is called a flag bit. If the value of the flag bit is False , then when the program executes wait(), it will be blocked; if the value of the flag bit is True , then the wait() method will no longer block. Note: Event locks cannot be used with the with statement, but can only be used in the usual way.

        This kind of lock is similar to traffic lights (the default is red light) , it belongs to block all threads at one time when the light is red, and release all queued threads at one time when the light is green.

        Event is one of the mechanisms for inter-thread communication: a thread sends an event signal, and other threads wait for this signal. That is, the main thread controls the execution of other threads .

        3.1 Construction method

        Event() : e = threading.Event() generates an event lock object

        3.2 Instance methods

        e.wait([timeout]) : Block the thread until the internal flag of the Event object is set to True or timeout, here you can choose whether to set the timeout parameter.

        set() : Set the flag bit to True .

        clear() : Set the flag bit to False .

        isSet() : Determine whether the flag is True .


4 Conditional locks 

        This mechanism will make threads wait, and only when a certain condition is met, n threads will be released .

        4.1 Construction method

        Condition(): c = threading.condition()

        4.2 Instance methods

        wait_for(func) : Wait for the function to return the result, if the result is True—release a thread

       wait(), c.notify(N) : release N waits at one time

        acquire() : lock          release() : unlock

        The wait and wait_for() above need to be used in the middle of locking and unlocking


5 Summary of the above locks

        1 Event locks are based on conditional locks. The difference between them and conditional locks is that only all of them can be released at one time, and any number of sub-threads cannot be released to continue running.

        2 Semaphore locks are also based on conditional locks. The differences between them and conditional locks and event locks are as follows:

             2.1 The conditional lock can release any number of threads in the "waiting" state at a time.

             2.2 The event lock can release all threads in the "waiting" state at one time.

             2.3 Semaphore locks , through regulations, release specific threads in the "locked" state in batches. 

        3 Event locks are internally based on conditional locks

        4 The internal semaphore lock is also based on conditional locks


6 GIL global interpreter lock

        The English abbreviation of the Global Interpreter Lock is GIL (Global Interpreter Lock). GIL has nothing to do with the Python language. It is just a problem left in the officially recommended interpreter CPython due to historical reasons. Why is CPython still officially recommended? Because the CPython community is very active, and it has a very rich library . Each thread needs to obtain GIL first during execution to ensure that only one thread can execute code at a time .

There are only two basic behaviors of         the GIL : 1. The currently executing thread holds the GIL 2. When the thread encounters IO blocking, it will release the GIL.

        Due to the limitation of CIL lock, multithreading is not suitable for computing tasks, but more suitable for IO tasks

        Computation-intensive tasks are frequent calculations by the CPU; IO-intensive tasks are network IO (capturing network data), disk operations (reading and writing files), keyboard input, etc.

        Computationally intensive tasks --> use multiple processes

        IO-intensive tasks --> use multithreading


 

Guess you like

Origin blog.csdn.net/m0_53891399/article/details/131523276