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 ...
随机推荐
- servlet的执行过程
第一次访问servlet的过程: 服务器启动:在服务器启动的时候,加载项目,就扫描web.xml文件,获得应用有哪些servlet,url-pattern, 客户端通过URl访问服务器[向服务器发送一 ...
- Redis 集群环境添加节点失败问题
最近在给公司网管系统Redis集群环境添加节点时候遇到一个问题,提示新增的Node不为空: [root@node00 src]# ./redis-trib.rb add-node --slave -- ...
- Java中字符串indexof() 的使用方法
Java中字符串中子串的查找共有四种方法(indexof())indexOf 方法返回一个整数值,指出 String 对象内子字符串的开始位置.如果没有找到子字符串,则返回-1.如果 startind ...
- 依赖layui form模块 复选框tree插件(拓展可根据属性单选还是多选,数据反选)
近些天接的项目用的是layui.以前没用过,踩了很多坑,坑就不多说了,直接说layui的tree.因为自带的tree不满足需求,所以在论坛.博客上找了很久终于找到了可以复选的的插件,原文地址:http ...
- linux下tomcat无法访问问题(换一种说法:无法访问8080端口)
有时候linux下的tomcat其他机器无法访问,比如主机无法访问linux虚拟机的tomcat,这是因为tocat的端口,linux没有对外开放,所以只能localhost访问,但是别的机器访问不了 ...
- 20155214&20155216 实验一 开发化境的熟悉
20155214&20155216 实验一 开发化境的熟悉 实验内容: 实验一 开发化境的熟悉-1-交叉编译环境-(使用实验室台式机) 1.建立实验目录"mkdir linux_组员 ...
- 敏捷冲刺每日报告——Day1
1.情况简述 Alpha阶段第一次Scrum Meeting 敏捷开发起止时间 2017.10.25 00:00 -- 2017.10.26 00:00 讨论时间地点 2017.10.25晚9:30, ...
- 冲刺NO.5
Alpha冲刺第五天 站立式会议 项目进展 今日项目完成内容主要包括了JS的学习,事务管理员模块与学生模块的完善与补充,并且开始编写信用信息管理模块和奖惩事务管理模块. 问题困难 前端部分的技术掌握的 ...
- Django 测试驱动开发
第一章 1.编写functional_tests.py from selenium import webdriver browser = webdriver.Firefox() browser.get ...
- 第四十八条:如果需要精确的答案,请避免使用float和double
让一个float或者double精确的表示0.1或者10的任何负数次方值都是不可能.float和double它们执行二进制浮点运算, 它们是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的 ...