Python 高级编程和异步IO并发编程 --11_4 线程同步(Lock/Rlock)

"""
线程同步
"""
def add1(a):
    a += 1   # 该语句对应4行字节码

def dec(a):
    a -= 1

import dis
print(dis.dis(add1))
print(dis.dis(dec))


add # 假设每执行一部切换一次。a为全局变量
"""
1. load a    #1  a=0
2. load 1    #3  1
3. +         #5  1
4. 赋值给a   #7  a=1
以上4步执行过程中,任何一步都可能会发生GIL释放,切换给其它线程。
"""

dec
"""
1. load a    #2 a=0
2. load 1    #4 1
3. -         #6 -1
4. 赋值给a   #8 a=-1
以上4步执行过程中,任何一步都可能会发生GIL释放,切换给其它线程。
"""
通过两个线程执行后,a最终呈现的结果,可能是1,也可能是-1,但a并并等于0; 我们希望的结果是,执行完毕后,a的结果为0
变量共享非常经典的问题:结果不一致。特别是电商web开发,减库存的时候,容易出现这个问题。
  5           0 LOAD_FAST                0 (a)     # 将a load到内存
              2 LOAD_CONST               1 (1)     # 将1 load到内存
              4 INPLACE_ADD                        # 执行相加
              6 STORE_FAST               0 (a)     # 将值赋值给a
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
None
  8           0 LOAD_FAST                0 (a)      # 将a load到内存
              2 LOAD_CONST               1 (1)      # 将1 load到内存
              4 INPLACE_SUBTRACT                    # 执行相减
              6 STORE_FAST               0 (a)      # 将值赋值给a
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
None

为了解决上述问题,希望add1执行时,其它代码段希望只有一个在执行。引出了线程同步机制。

"""
线程同步
"""
from threading import Lock  # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading

lock = Lock()
total = 0

def add():
    global total
    global lock
    for i in range(100):
        lock.acquire()   # 获取锁后,别的线程无法获取这把锁,除非它释放锁
        total += 1
        lock.release()   # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行

def dec():
    global total
    global lock
    for i in range(100):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()

thread1.join()  # Wait until the thread terminates.
thread2.join()
print(total)

"""
1. 锁会影响性能
2. 锁会引起死锁
"""
"""
死锁范例1
"""
from threading import Lock  # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading

lock = Lock()
total = 0

def add():
    global total
    global lock
    for i in range(100):
        lock.acquire()
        lock.acquire()   # 已经死锁。 
        total += 1
        lock.release()   # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行

def dec():
    global total
    global lock
    for i in range(100):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()

thread1.join()  # Wait until the thread terminates.
thread2.join()
print(total)

'''
死锁范例2: 资源竞争

A(a,b)
acquire (a)
acquire (b)

B(a,b)
acquire(b)
acquire(a)

对于A/B,只有同时获取a/b才可运行。如果资源a被A获取,同时资源b被B获取,这样会导致A/B都无法运行造成死锁,上述问题解决办法:让B也是先拿a,再拿b

A(a,b)
acquire (a)
acquire (b)

B(a,b)
acquire(a)
acquire(b)
'''
"""
死锁范例3
"""
from threading import Lock  # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
import threading

lock = Lock()
total = 0

def add():
    global total
    global lock
    for i in range(100):
        lock.acquire()   # 由死锁范例1可知,acquire不可连续调用两次
        dosomething()
        total += 1
        lock.release()   # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
        
def dosomething():
    lock.acquire()
    # do something
    lock.release()
"""
线程同步
"""
from threading import Lock  # 凡是加锁后,只允许同时一个代码段运行,运行结束后,其它代码段才开始运行。
from threading import RLock  # 可重入的锁
import threading

"""RLock允许在同一个线程里面,可以连续调用多次acquire,但是一定要注意,acquire的次数与release的次数相等
线程之间还是竞争关系"""
lock = RLock()
total = 0

def add():
    global total
    global lock
    for i in range(100):
        lock.acquire()
        lock.acquire()
        total += 1
        lock.release()   # 获取锁后,运行完成后必须释放,否则会导致其它代码段无法运行
        lock.release()

def dec():
    global total
    global lock
    for i in range(100):
        lock.acquire()
        total -= 1
        lock.release()

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=dec)
thread1.start()
thread2.start()

thread1.join()  # Wait until the thread terminates.
thread2.join()
print(total)    # 0

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值