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 ...
随机推荐
- 前端touch事件方向的判断
移动端touch事件判断滑屏手势的方向 方法一 当开始一个touchstart事件的时候,获取此刻手指的横坐标startX和纵坐标startY: 当触发touchmove事件时,在获取此时手指的横坐标 ...
- 使用domain模块捕获异步回调中的异常
和其他服务器端语言相比,貌似node.js 对于异常捕捉确实非常困难. 首先你会想到try/catch ,但是在使用过程中我们会发现并没有真正将错误控制在try/catch 语句中. 为什么? 答案是 ...
- 【Python】 迭代器&生成器
迭代器 任何一个类,只要其实现了__iter__方法,就算是一个可迭代对象.可迭代对象的__iter__方法返回的对象是迭代器,迭代器类需要实现next方法.一般来说,实现了__iter__方法的类肯 ...
- [bzoj1497][NOI2006]最大获利_网络流_最小割
最大获利 bzoj-1497 题目大意:可以建立一个点,花费一定的代价:将已经建立的两个点之间连边,得到一定收益.有些节点之间是不允许连边的. 注释:1<=点数<=5,000,1<= ...
- python安装的时候报SSL连接错误的解决办法
Collecting xlwt Could not fetch URL https://pypi.python.org/simple/xlwt/: There was a problem conf ...
- 一周Maven框架学习随笔
第一次写博客,可能写得不是很好,但是希望自己持之以恒,以后会更好.也希望通过写博客记录随笔,让自己本身有所收获. 下面是今天的maven总结: maven个人理解中是Maven项目对象模型(POM), ...
- js通过a链接控制多个DIV只显示其中一个其它隐藏
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- SSDB安装配置 ERROR! autoconf required! install autoconf first
SSDB简介 SSDB是一个C/C++语言开发的高性能开源NoSQL数据库服务器,支持Key-value, Keyhashmap, Key-zset(sorted set) 等数据结构,十分适合存储数 ...
- 冲刺总结随笔(Alpha)
冲刺总结随笔 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.项目预期进展及现实进展 项目预期 ...
- Linux中Eclipse下搭建Web开发环境
0. 准备工作 java环境,Linux下基本上都有含开源jdk的库,可直接下载,且不用配置环境变量,当然也可以官网下载后自己配置: Eclipse Neon,注意看清是64位还是32位,下载的应该是 ...