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. Android短信验证码倒计时

    有两种实现方法 1.第一种方式:Timer /** * Description:自定义Timer * <p> * Created by Mjj on 2016/12/4. */ publi ...

  2. linux-2.6.18源码分析笔记---信号

    一.相关数据结构及其位置(大致浏览即可,介绍流程时再来仔细看) 1.1 进程描述符struct task_struct所在目录:include\linux\sched.h 关注task_struct中 ...

  3. 【Python】 汉字转化汉语拼音pinyin

    pinyin pinyin模块是github上一个小项目,在github.com/cleverdeng/pinyin.py上面可以下到源码.衷心感谢那些为这个模块做出贡献的人来方便我们[鞠躬] 安装: ...

  4. JVM学习八:常用JVM配置参数

    前面学习的都是和类加载相关的知识,接下来学习的则和GC相关的知识,都是JVM的几个重点块. 零.在IDE的后台打印GC日志: 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是 ...

  5. JavaOOP-继承

    继承 1.继承的概念 继承是类与类之间的关系,是一个很简单很直观的概念,与现实世界中的继承(例如儿子继承父亲财产)类似. 继承可以理解为一个类从另一个类获取方法和属性的过程.如果类B继承于类A,那么B ...

  6. 关于php日期前置是否有0

    例如:2018-01-04,这个日期和月份前置是有0 如果不想有0,date( 'y-n-j',time() ):默认的是date( 'y-m-d',time() ),这个日期和月份前置是有0. da ...

  7. 『备注』&#x; 格式 的编码转换

    在很多 网站(或者很多 WebService), 我们总能看到 Ӓ &#A22A;  这种格式 的编码. 如何将这种编码 转换成 实际文本,C#代码如下: //各种 幺蛾子网页图标 请参见: ...

  8. Leetcode 6——ZigZag Conversion

    The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like ...

  9. JavaScript(第十三天)【内置对象】

    学习要点: 1.Global对象 2.Math对象 ECMA-262对内置对象的定义是:"由ECMAScript实现提供的.不依赖宿主环境的对象,这些对象在ECMAScript程序执行之前就 ...

  10. 利用jmeter做一个简单的性能测试并进行参数化设置

    1.新增一个线程组,并在下面添加基本原件,包括:监听器.http请求默认值和一个事务控制器 在http请求默认值中填写 ip 地址和端口号,协议类型默认为http 2.添加代理服务器,以便之后进行录制 ...