Python系列之 - 锁(GIL,Lock,Rlock,Event,信号量)
python 的解释器,有很多种,但市场占有率99.9%的都是基于c语言编写的CPython. 在这个解释器里规定了GIL。
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
意思:无论有多少个cpu,python在执行时会淡定的在同一时刻只允许一个线程运行。(一个进程可以开多个线程,但线程只能同时运行一个)
下面举例子:
def add():
sum=0
for i in range(10000000):
sum+=i
print("sum",sum) def mul():
sum2=1
for i in range(1,100000):
sum2*=i
print("sum2",sum2)
import threading,time
start=time.time() t1=threading.Thread(target=add)
t2=threading.Thread(target=mul) l=[]
l.append(t1)
l.append(t2)
print(time.ctime())
add()
mul() print("cost time %s"%(time.ctime()))
执行结果
Sat Apr 14 20:10:39 2018
sum 49999995000000
sum2 282422940796034787429342157802453551847
cost time Sat Apr 14 20:10:50 2018 sum2太大,截取了一部分
可已看出串行需要11秒,(-_-我还开着一些别的软件,需要占用大量内存)
然后我们用多线程分别执行两个程序
def add():
sum=0
for i in range(10000000):
sum+=i
print("sum",sum) def mul():
sum2=1
for i in range(1,100000):
sum2*=i
print("sum2",sum2)
import threading,time
start=time.time() t1=threading.Thread(target=add)
t2=threading.Thread(target=mul) l=[]
l.append(t1)
l.append(t2)
print(time.ctime())
for t in l:
t.start()
for t in l:
t.join()
print("cost time %s"%(time.ctime()))
执行结果:
Sat Apr 14 20:14:51 2018
sum 49999995000000
sum2 28242294079603478742934215780245355
cost time Sat Apr 14 20:15:01 2018 可以看出执行速度加快了。
但是减少的时间不是很多。这是为什么呢?
首先我们需要知道任务类型分两种:CPU密集型、IO密集型
像上面的例子就是CPU密集型,需要大量的计算
而另一种就是需要频繁的进行输入输出(一遇到IO,就切换)
接着写一个IO密集型的例子:
import threading
import time
def music():
print("begin to listen %s"%time.ctime())
time.sleep(3)
print("stop to listen %s" % time.ctime()) def game(): print("begin to play game %s"%time.ctime())
time.sleep(5)
print("stop to play game %s" % time.ctime()) if __name__ == '__main__':
t1= threading.Thread(target=music)
t2 = threading.Thread(target=game)
t1.start()
t2.start()
t1.join()
t2.join()
print("ending")
输出结果:
begin to listen Sat Apr 14 20:23:03 2018
begin to play game Sat Apr 14 20:23:03 2018
stop to listen Sat Apr 14 20:23:06 2018
stop to play game Sat Apr 14 20:23:08 2018
ending Process finished with exit code 0
import threading
import time
def music():
print("begin to listen %s"%time.ctime())
time.sleep(3)
print("stop to listen %s" % time.ctime()) def game(): print("begin to play game %s"%time.ctime())
time.sleep(5)
print("stop to play game %s" % time.ctime()) if __name__ == '__main__':
music()
game()
# t1= threading.Thread(target=music)
# t2 = threading.Thread(target=game)
# t1.start()
# t2.start()
# t1.join()
# t2.join()
print("ending")
输出结果:
begin to listen Sat Apr 14 20:24:44 2018
stop to listen Sat Apr 14 20:24:47 2018
begin to play game Sat Apr 14 20:24:47 2018
stop to play game Sat Apr 14 20:24:52 2018
ending Process finished with exit code 0
很明显 对于IO密集型多线程的优势非常明显.
| 同步锁Lock |
多个线程都在同时操作同一个共享资源,所以造成了资源破坏,怎么办呢?(join会造成串行,失去所线程的意义)
import time
import threading
def addNum():
global num #在每个线程中都获取这个全局变量
temp=num
time.sleep(0.0001)
num =temp-1 #对此公共变量进行-1操作
num = 100 #设定一个共享变量
thread_list = []
for i in range(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)
for t in thread_list: #等待所有线程执行完毕
t.join()
print('final num:', num )
结果:
final num: 87 Process finished with exit code 0
线程之间竞争资源,谁抢到谁执行
我们可以通过同步锁来解决这种问题 R=threading.Lock() ####
def sub():
global num
R.acquire()
temp=num-1
time.sleep(0.1)
num=temp
R.release()
# 即运行到此就变成了串行,本语言无力改变
| 线程死锁和递归锁RLock |
import threading,time
class myThread(threading.Thread):
def doA(self):
lockA.acquire()
print(self.name,"gotlockA",time.ctime())
time.sleep(3)
lockB.acquire()
print(self.name,"gotlockB",time.ctime())
lockB.release()
lockA.release() def doB(self):
lockB.acquire()
print(self.name,"gotlockB",time.ctime())
time.sleep(2)
lockA.acquire()
print(self.name,"gotlockA",time.ctime())
lockA.release()
lockB.release() def run(self):
self.doA()
self.doB()
if __name__=="__main__": lockA=threading.Lock()
lockB=threading.Lock()
threads=[]
for i in range(5):
threads.append(myThread())
for t in threads:
t.start()

结果就卡到这里了,?,Thread-1申请lockB|Thread-2申请lockB,但是两者都申请不到于是产生了死锁
于是------当某个线程申请到一个锁,其余线程不能再申请。于是有了递归锁(其实就是内部维护了一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次acquire。直到一个线程所有的acquire都被release,其他的线程才能获得资源。)
import threading,time
class myThread(threading.Thread):
def doA(self):
R_lock.acquire()
print(self.name,"gotlockA",time.ctime())
time.sleep(3)
R_lock.acquire()
print(self.name,"gotlockB",time.ctime())
R_lock.release()
R_lock.release() def doB(self):
R_lock.acquire()
print(self.name,"gotlockB",time.ctime())
time.sleep(2)
R_lock.acquire()
print(self.name,"gotlockA",time.ctime())
R_lock.release()
R_lock.release() def run(self):
self.doA()
self.doB()
if __name__=="__main__": R_lock = threading.RLock()
threads=[]
for i in range(5):
threads.append(myThread())
for t in threads:
t.start()
结果:

| 同步条件(Event) |
An event is a simple synchronization object;the event represents an internal flag,
and threads can wait for the flag to be set, or set or clear the flag themselves.
事件是一个简单的同步对象;事件表示一个内部标志,
线程可以等待设置标志,或设置或清除标志本身。
event = threading.Event()
#客户机线程可以等待设置标志。
# a client thread can wait for the flag to be set
event.wait()
一个服务器线程可以设置或重置它。
# a server thread can set or reset it
event.set()
event.clear()
如果设置了标志,等待方法不会执行任何操作。
如果标记被清除,等待将阻塞直到它再次被设置。
任何数量的线程都可以等待相同的事件。
If the flag is set, the wait method doesn’t do anything.
If the flag is cleared, wait will block until it becomes set again.
Any number of threads may wait for the same event. import threading,time
class Boss(threading.Thread):
def run(self):
print("BOSS:今晚大家都要加班到22:00。")
print(event.isSet())
event.set()
time.sleep(5)
print("BOSS:<22:00>可以下班了。")
print(event.isSet())
event.set()
class Worker(threading.Thread):
def run(self):
event.wait()
print("Worker:哎……命苦啊!")
time.sleep(1)
event.clear()
event.wait()
print("Worker:OhYeah!")
if __name__=="__main__":
event=threading.Event()
threads=[]
for i in range(5):
threads.append(Worker())
threads.append(Boss())
for t in threads:
t.start()
for t in threads:
t.join()

| 信号量 |
信号量用来控制线程并发数的,BoundedSemaphore或Semaphore管理一个内置的计数 器,每当调用acquire()时-1,调用release()时+1。
计数器不能小于0,当计数器为 0时,acquire()将阻塞线程至同步锁定状态,直到其他线程调用release()。(类似于停车位的概念)
BoundedSemaphore与Semaphore的唯一区别在于前者将在调用release()时检查计数 器的值是否超过了计数器的初始值,如果超过了将抛出一个异常。
import threading,time
class myThread(threading.Thread):
def run(self):
if semaphore.acquire():
print(self.name)
time.sleep(5)
semaphore.release()
if __name__=="__main__":
semaphore=threading.Semaphore(5)
thrs=[]
for i in range(100):
thrs.append(myThread())
for t in thrs:
t.start()

Python系列之 - 锁(GIL,Lock,Rlock,Event,信号量)的更多相关文章
- Python3学习之路~9.3 GIL、线程锁之Lock\Rlock\信号量、Event
一 Python GIL(Global Interpreter Lock) 全局解释器锁 如果一个主机是单核,此时同时启动10个线程,由于CPU执行了上下文的切换,让我们宏观上看上去它们是并行的,但实 ...
- 【python】多进程锁multiprocess.Lock
[python]多进程锁multiprocess.Lock 2013-09-13 13:48 11613人阅读 评论(2) 收藏 举报 分类: Python(38) 同步的方法基本与多线程相同. ...
- python类库32[多进程同步Lock+Semaphore+Event]
python类库32[多进程同步Lock+Semaphore+Event] 同步的方法基本与多线程相同. 1) Lock 当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突. imp ...
- Python全局解释器锁 -- GIL
首先强调背景: 1.GIL是什么?GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定. 2.每个CPU在同一时间只能 ...
- python 全局解释锁GIL
Python的全局解释器锁GIL用于保护python解释器,使得任意时刻,只有一个线程在解释器中运行.从而保证线程安全 在多线程环境中,Python 虚拟机按以下方式执行: 1. 设置GIL2. 切换 ...
- Python核心技术与实战——十九|一起看看Python全局解释器锁GIL
我们在前面的几节课里讲了Python的并发编程的特性,也了解了多线程编程.事实上,Python的多线程有一个非常重要的话题——GIL(Global Interpreter Lock).我们今天就来讲一 ...
- Day12- Python基础12 线程、GIL、Lock锁、RLock锁、Semaphore锁、同步条件event
http://www.cnblogs.com/yuanchenqi/articles/6248025.html 博客地址 本节内容: 1:进程和线程的说明 2:线程的两种调用方式 3:threadi ...
- 扯扯python的多线程的同步锁 Lock RLock Semaphore Event Condition
我想大家都知道python的gil限制,记得刚玩python那会,知道了有pypy和Cpython这样的解释器,当时听说是很猛,也就意味肯定是突破了gil的限制,最后经过多方面测试才知道,还是那德行… ...
- python 多线程中的同步锁 Lock Rlock Semaphore Event Conditio
摘要:在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lo ...
随机推荐
- from提交表单后 数据提交到后台 但不跳转页面 可用iframe
可以页面事先加载被隐藏的iframe标签,或者等到需要的时候通过js生成,再提交,提交之前,form的target指向iframe(我是要实现新页面生成的时候程半透明状态,所以用了后者的方法) 代码如 ...
- CXF对Interceptor拦截器的支持
前面在Axis中介绍过Axis的Handler,这里CXF的Interceptor就和Handler的功能类似.在每个请求响应之前或响应之后,做一些事情.这里的Interceptor就和Filter. ...
- 彻底弄懂CommonJS和AMD/CMD!
JS中的模块规范(CommonJS,AMD,CMD),如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已. 现在就看看吧, ...
- 借鉴别人的Oracle 11g安装和卸载图文教程
Oracle 11g安装 1.解压下载的包,然后进入包内,点击setup.exe开始安装 . 2.出现如下:一般把那个小对勾取消,点击下一步进行, 弹出下图这个后点‘是' 3.下图后,选择创建和配置数 ...
- react native 增量升级方案(转)
前言 facebook的react-native给我们带来了用js写出原生应用的同时,也使得使用RN编写的代码的在线升级变得可能,终于可以不通过应用市场来进行升级,极大的提升了app修bug和赋予新功 ...
- (译文)开始学习Webpack-应用TypeScript,配置热加载和Source Map
项目初始化:采用TypeScript 我们的版本是: $ node --version v8.5.0 $ npm --version 5.5.1 npm版本升级了,因为npm最近带来了新特性,本地会生 ...
- C语言的第 次作业总结
PTA实验作业 第一题: 使用函数输出水仙花数 1.设计思路: 2.碰到的问题及解决方法: 实验中碰到的主要问题是:虽然知道如何求每一位的数但不知道如何输出m到n之间的水仙花数,我上面截图中的和瓮恺视 ...
- 2017-2018-1 Java演绎法 第四五周 作业
团队任务:撰写<需求规格说明书> 团队组长:袁逸灏 本次编辑:刘伟康 流程.分工.比例 (比例按照任务的费时.难度和完成情况估算) 流程 确定任务 -→ 分配任务 -→ 各组员完成各自任务 ...
- 201621123035 《Java程序设计》第1周学习总结
1.本周学习总结 本周学习内容:Java平台概论.认识JDK规范与操作.了解JVM.JRE与JDK.撰写Java原始码.path是什么 关键词:JVM.JRE.JDK 联系:JVM是Java虚拟机的缩 ...
- 小黄衫 Get
小黄衫 Get . 十分荣幸在前四次作业中以微弱的3分之差拿到了第一,获得了本次的小黄衫. 先发点牢骚.. 讲道理,原本以为的研究生生涯应该就是埋在论文堆里度过的时候顺便上上课.当初选课的时候,学 ...