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,信号量)的更多相关文章

  1. Python3学习之路~9.3 GIL、线程锁之Lock\Rlock\信号量、Event

    一 Python GIL(Global Interpreter Lock) 全局解释器锁 如果一个主机是单核,此时同时启动10个线程,由于CPU执行了上下文的切换,让我们宏观上看上去它们是并行的,但实 ...

  2. 【python】多进程锁multiprocess.Lock

    [python]多进程锁multiprocess.Lock 2013-09-13 13:48 11613人阅读 评论(2) 收藏 举报  分类: Python(38)  同步的方法基本与多线程相同. ...

  3. python类库32[多进程同步Lock+Semaphore+Event]

    python类库32[多进程同步Lock+Semaphore+Event]   同步的方法基本与多线程相同. 1) Lock 当多个进程需要访问共享资源的时候,Lock可以用来避免访问的冲突. imp ...

  4. Python全局解释器锁 -- GIL

    首先强调背景: 1.GIL是什么?GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定. 2.每个CPU在同一时间只能 ...

  5. python 全局解释锁GIL

    Python的全局解释器锁GIL用于保护python解释器,使得任意时刻,只有一个线程在解释器中运行.从而保证线程安全 在多线程环境中,Python 虚拟机按以下方式执行: 1. 设置GIL2. 切换 ...

  6. Python核心技术与实战——十九|一起看看Python全局解释器锁GIL

    我们在前面的几节课里讲了Python的并发编程的特性,也了解了多线程编程.事实上,Python的多线程有一个非常重要的话题——GIL(Global Interpreter Lock).我们今天就来讲一 ...

  7. Day12- Python基础12 线程、GIL、Lock锁、RLock锁、Semaphore锁、同步条件event

    http://www.cnblogs.com/yuanchenqi/articles/6248025.html  博客地址 本节内容: 1:进程和线程的说明 2:线程的两种调用方式 3:threadi ...

  8. 扯扯python的多线程的同步锁 Lock RLock Semaphore Event Condition

    我想大家都知道python的gil限制,记得刚玩python那会,知道了有pypy和Cpython这样的解释器,当时听说是很猛,也就意味肯定是突破了gil的限制,最后经过多方面测试才知道,还是那德行… ...

  9. python 多线程中的同步锁 Lock Rlock Semaphore Event Conditio

    摘要:在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lo ...

随机推荐

  1. poj1321 棋盘(dfs)

    #include<iostream> #include<cstring> using namespace std; ]={},sum=; ][]; void dfs(int a ...

  2. Oracle中死锁与等待

    在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(即S锁).当数据对象被加上排它锁时,其他的事务不能不  能对它读取和修改.加了共享锁的数据对象可以被其他事务读取 ...

  3. 关于Unity中如何代码动态修改天空盒

    在Unity中动态修改天空盒有两种方法: 一.为每个Texture建立天空盒材质球,需要更换时直接将对应材质球作为天空盒,缺点是建立的材质球太多 private void ChangeSkybox(M ...

  4. IntelliJ IDEA的入门使用

    1 修改背景颜色 点击File,Settings 2 修改字体格式,以及字体大小 我个人习惯Eclipse的默认字体,所有把字体修改成Eclipse默认的Consolas 同上点击File,Setti ...

  5. JavaScript简史

    JavaScript诞生于1995年. 当时的web正在日益兴起,人们对客户端语言的需求也越来越强烈.当时走在技术革新最前沿的Netscape公司决定开发一种客户端语言,用来处理简单的输入验证. 当时 ...

  6. Beta版本敏捷冲刺每日报告——Day1

    1.情况简述 Beta阶段第一次Scrum Meeting 敏捷开发起止时间 2017.11.2 08:00 -- 2017.11.2 21:00 讨论时间地点 2017.11.2晚6:00,软工所实 ...

  7. python functools.lru_cache做备忘功能

    import time import functools def clock(func): @functools.wraps(func)#还原被装饰函数的__name__和__doc__属性 def ...

  8. TSP-旅行商问题

    #include <iostream> #include <vector> #include <algorithm> using namespace std; in ...

  9. VMware虚拟机误删除vmdk文件后如何恢复?

    故障描述: Dell R710系列服务器(用于VMware虚拟主机),Dell MD 3200系列存储(用于存放虚拟机文件),VMware ESXi 5.5版本,因意外断电,导致某台虚拟机不能正常启动 ...

  10. 原始的Ajax方法 (异步的 JavaScript 和 XML -- (Extensible Markup Language 可扩展标记语言))

    <script language="javascript" type="text/javascript"> var request = false; ...