educoder 使用线程锁(lock)实现线程同步_Python并行编程(二):多线程锁机制利用Lock与RLock实现线程同步...

a6dcb87194be837a59428ffe041d0492.png

什么是锁机制?

要回答这个问题,我们需要知道为什么需要使用锁机制。前面我们谈到一个进程内的多个线程的某些资源是共享的,这也是线程的一大优势,但是也随之带来一个问题,即当两个及两个以上的线程同时访问共享资源时,如果此时没有预设对应的同步机制,就可能带来同一时刻多个线程同时访问同一个共享资源,即出现竞态,多数情况下我们是不希望出现这样的情况的,那么怎么避免呢?

Lock() 管理线程

先看一段代码:

import threadingimport timeresource = 0count = 1000000resource_lock = threading.Lock()def increment(): global resource for i in range(count): resource += 1def decerment(): global resource for i in range(count): resource -= 1increment_thread = threading.Thread(target=increment)decerment_thread = threading.Thread(target=decerment)increment_thread.start()decerment_thread.start()increment_thread.join()decerment_thread.join()print(resource)

运行截图如下:

c691af7627ef38dd1df2d7a157df5293.png

运行结果

当我们多次运行时,可以看到最终的结果都几乎不等于我们期待的值即resource初始值0。为什么呢? 原因就是因为 += 和 -=并不是原子操作。可以使用dis模块查看字节码:

import disdef add(total): total += 1def desc(total): total -= 1total = 0print(dis.dis(add))print(dis.dis(desc))# 运行结果:# 3 0 LOAD_FAST 0 (total)# 3 LOAD_CONST 1 (1)# 6 INPLACE_ADD# 7 STORE_FAST 0 (total)# 10 LOAD_CONST 0 (None)# 13 RETURN_VALUE# None# 5 0 LOAD_FAST 0 (total)# 3 LOAD_CONST 1 (1)# 6 INPLACE_SUBTRACT# 7 STORE_FAST 0 (total)# 10 LOAD_CONST 0 (None)# 13 RETURN_VALUE# None

那么如何保证初始值为0呢? 我们可以利用Lock(),代码如下:

import threadingimport timeresource = 0count = 1000000resource_lock = threading.Lock()def increment(): global resource for i in range(count): resource_lock.acquire() resource += 1 resource_lock.release()def decerment(): global resource for i in range(count): resource_lock.acquire() resource -= 1 resource_lock.release()increment_thread = threading.Thread(target=increment)decerment_thread = threading.Thread(target=decerment)increment_thread.start()decerment_thread.start()increment_thread.join()decerment_thread.join()print(resource)

运行截图如下:

e3c56f6ac4f9c5a5b2914d2c462ef426.png

运行结果

从运行结果可以看到,不论我们运行多少次改代码,其resource的值都为初始值0, 这就是Lock()的功劳,即它可以将某一时刻的访问限定在单个线程或者单个类型的线程上,在访问锁定的共享资源时,必须要现获取对应的锁才能访问,即要等待其他线程释放资源,即resource_lock.release()

当然为了防止我们对某个资源锁定后,忘记释放锁,导致死锁,我们可以利用上下文管理器管理锁实现同样的效果:import threadingimport timeresource = 0count = 1000000resource_lock = threading.Lock()def increment(): global resource for i in range(count): with resource_lock: resource += 1def decerment(): global resource for i in range(count): with resource_lock: resource -= 1increment_thread = threading.Thread(target=increment)decerment_thread = threading.Thread(target=decerment)increment_thread.start()decerment_thread.start()

RLock() 与Lock()的区别

我们需要知道Lock()作为一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,否则会发生死锁:

import threadingresource.lock = threading.lock()resource = 0resource.lock.acquire()resource.lock.acquire()resource += 1resource.lock.release()resource.lock.release()

为解决同一线程中不能多次请求同一资源的问题,python提供了“可重入锁”:threading.RLock,RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源 。用法和threading.Lock类相同,即比如递归锁的使用:

import threadinglock = threading.RLock()def dosomething(lock): lock.acquire() # do something lock.release()lock.acquire()dosomething(lock)lock.release()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值