Python作为一门比较常见的编程语言,可以对其进行多线程的编程,包括利用Lock与RLock,实现多线程之间的相互同步,那么,实现这种原理的机制到底是什么样子的呢?下面就给大家详细解答下。
什么是锁机制?
要回答这个问题,我们需要知道为什么需要使用锁机制。前面我们谈到一个进程内的多个线程的某些资源是共享的,这也是线程的一大优势,但是也随之带来一个问题,即当两个及两个以上的线程同时访问共享资源时,如果此时没有预设对应的同步机制,就可能带来同一时刻多个线程同时访问同一个共享资源,即出现竞态,多数情况下我们是不希望出现这样的情况的,那么怎么避免呢?
Lock()管理线程
先看一段代码:
import threading import time resource=0 count=1000000 resource_lock=threading.Lock() def increment(): global resource for i in range(count): resource+=1 def decerment(): global resource for i in range(count): resource-=1 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)
运行截图如下:
运行结果
当我们多次运行时,可以看到最终的结果都几乎不等于我们期待的值即resource初始值0。
为什么呢?原因就是因为+=和-=并不是原子操作。
可以使用dis模块查看字节码:
import dis def add(total): total+=1 def desc(total): total-=1 total=0 print(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 threading import time resource=0 count=1000000 resource_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)
运行截图如下:
运行结果
从运行结果可以看到,不论我们运行多少次改代码,其resource的值都为初始值0,这就是Lock()的功劳,即它可以将某一时刻的访问限定在单个线程或者单个类型的线程上,在访问锁定的共享资源时,必须要现获取对应的锁才能访问,即要等待其他线程释放资源,即resource_lock.release()当然为了防止我们对某个资源锁定后,忘记释放锁,导致死锁,我们可以利用上下文管理器管理锁实现同样的效果:
import threading import time resource=0 count=1000000 resource_lock=threading.Lock() def increment(): global resource for i in range(count): with resource_lock: resource+=1 def decerment(): global resource for i in range(count): with resource_lock: resource-=1 increment_thread=threading.Thread(target=increment) decerment_thread=threading.Thread(target=decerment) increment_thread.start() decerment_thread.start()
RLock()与Lock()的区别
我们需要知道Lock()作为一个基本的锁对象,一次只能一个锁定,其余锁请求,需等待锁释放后才能获取,否则会发生死锁:
import threading resource.lock=threading.lock() resource=0 resource.lock.acquire() resource.lock.acquire() resource+=1 resource.lock.release() resource.lock.release()
为解决同一线程中不能多次请求同一资源的问题,python提供了“可重入锁”:threading.RLock,RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。
直到一个线程所有的acquire都被release,其他的线程才能获得资源。用法和threading.Lock类相同,即比如递归锁的使用:
import threading lock=threading.RLock() def dosomething(lock): lock.acquire() #do something lock.release() lock.acquire() dosomething(lock) lock.release()
综上所述,这篇文章就给大家介绍到这里了,希望可以给大家带来帮助。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/128429.html
摘要:如果某线程并未使用很多操作,它会在自己的时间片内一直占用处理器和。在中使用线程在和等大多数类系统上运行时,支持多线程编程。守护线程另一个避免使用模块的原因是,它不支持守护线程。 这一篇是Python并发的第四篇,主要介绍进程和线程的定义,Python线程和全局解释器锁以及Python如何使用thread模块处理并发 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条...
摘要:某进程内的线程在其它进程不可见。线程的实体包括程序数据和。包括以下信息线程状态。当线程不运行时,被保存的现场资源。用户级线程执行系统调用指令时将导致其所属进程被中断,而内核支持线程执行系统调用指令时,只导致该线程被中断。线程能够利用的表空 操作系统线程理论 线程概念的引入背景 进程之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运...
摘要:本文最先发布在博客这篇文章将讲解并发编程的基本操作。并发是指能够多任务处理,并行则是是能够同时多任务处理。虽然自带了很好的类库支持多线程进程编程,但众所周知,因为的存在,很难做好真正的并行。 本文最先发布在博客:https://blog.ihypo.net/151628... 这篇文章将讲解 Python 并发编程的基本操作。并发和并行是对孪生兄弟,概念经常混淆。并发是指能够多任务处...
阅读 889·2023-01-14 11:38
阅读 833·2023-01-14 11:04
阅读 684·2023-01-14 10:48
阅读 1887·2023-01-14 10:34
阅读 891·2023-01-14 10:24
阅读 750·2023-01-14 10:18
阅读 479·2023-01-14 10:09
阅读 519·2023-01-14 10:02