python进程之间全局锁

标签:
python |
分类: python基础 |
想要理解全局锁,必须的要理解并发,并行的概要。
time.sleep(10)
print("hello",name,time.ctime())
p_list=[]
for i in range(3):
#创建进程。调用该包的process函数,来创建一个进程。
p=Process(target=f,args=("jack"))
#定义一个列表,将进程3次加入列表里面。
p_list.append(p)
#开启进程
p.start()
#循环列表。
for i in p_list:
#给进程添加join方法之后,可以看到进程一次会被执行。
p.join()
print("end of it ....")
def
__init__(self,name):
#调用其父类的方法。
super(MyProcess,self).__init__()
self.name=name
def run(self):
time.sleep(10)
print("hello"+self.name)
print(time.ctime())
p_list=[]
for i in range(3):
p=MyProcess("jack")
p.start()
p_list.append(p)
for p in p_list:
p.join()
print("end of ......")
什么是并发,并发值得就是在同一时刻,多个程序都在运行。多个进程都在启动。这就是所谓的并发。
并行指的就是在同一个时间点,多个程序在运行。由此可以看出并行是并发的子集。
同时还要理解同步和异步的概要:
什么是同步:当一个程序在运行的时候,后面的程序都会停止在哪里,直到该程序执行完毕,才能执行后面的
的程序。
什么是异步:当一个程序在执行的时候,后面的程序可以继续的执行。cpu将时间片切换到后面的程序,当该程序执行完毕之后,才会通知cpu进行进一步的处理,异步,大大的提高了
cpu的利用率
今天,我们来看看python的进程之间通讯的核心部分,也就是我们的全局锁。
GIL(global interper Lack)就是全局解释器锁。来源是python设计之初的考虑。
主要是为了数据安全所做的 决定。
每一个cpu在同一时期,只能执行一个线程,(在单核cpu下面的多线程其实是并发的。并不是并行的,并行和并发都是
处理多路请求的概要,但是两者又者本质的区别。并行是指两个后者多个事件发生在同一时刻,但是并发
指得就是两个或者多个
在同一时间间隔内部发生。
python的全局锁的概要。
GIL的早期的设计;
python支持多线程。然而,解决多线程之间数据的完整性和状态的同步的最简单的方法就是加锁。
于是就有了GIL这把超级大锁,然而,当越来越多的代码库开发者接受了这种设定之后,
他们开始大量的依赖这种特性。(及默认的内部对象是thread-safe,并不需要额外的同步锁
和内存的操作,)慢慢的这种方式,被发现是蛋疼的,并且是低效的。但是当大家试图去拆分和去除
GIL的时候,发现大量的代码库已经重度依赖GIL,然而非常的难以去除了,有多难了,打个简单
的比方,像MYSQL这样的小项目,为了把BUffer pool
Mute 这把大锁拆分为小锁,花费从
5.5 到5.6 到5.7多个大版,为期将近5年的时间,并且让在继续,MYSQL有固定的开发团队的产品
走的如此的艰难,那么更何况python这样的核心开发和代码贡献者高度社区化的团队了。很明显花费的
时间会更加的长。
在python的多线程下面,每个线程的执行方式:
1.获取GIL
2.执行代码直到sleep或者是python的虚拟机将其挂起
3.释放GIL
可见,某个线程想要执行,必须拿到GIL,我们可以把GIL看作是通行证,并且在一个python
进程里面,GIL只有一个,拿不到通行证的线程,就不容许进入到cpu里面去执行。
然而,每次释放GIL锁,进程之间进行锁竞争,切换线程,会消耗资源,并且由于GIL锁的存在,
python里面的一个进程,只能同时执行一个线程,(拿到GIL的线程才会执行),这就是为何在
多核cpu上面,python的多线程效率不高的原因。
那么在这里,想说的就是是不是python的多线程是不是没有作用了。
在这里,我们将分类讨论:
1.cpu密集型代码。又称之为计算密集型任务,是指cpu计算占主要的任务,cpu一直处于满负荷的
状态,比如在一个很大的列表里面查找元素,(当然,这是不合理的),复杂的加减乘除等等各种循环处理计数等等
在这种情况下ticks计数很快就会达到阀值,然后触发GIL的释放和在竞争,所以python的多线程
对cpu的密集型代码并不友好
2.io密集型代码,指的就是磁盘io,网络io占据主要的任务,计算量很小的代码。比如请求网页,读取文件等等
当然我们可以利用sleep达到io密集型任务。
文件处理,网路爬虫,多线程能够有效的提升效率,单线程下有io操作,就会进行io等待,造成不必要的时间浪费
然而,开启多线程能够在线程A等待的情况下面,自动切换到线程到线程B,可以不浪费cpu的资源,从而提高代码执行的
执行效率。多以python的多线程对io密集型的代码比较友好。
3.在python
3.x中GIL不适用ticks计数。改为使用计时器。执行时间达到阀值之后,当前线程释放GIL,
这样对cpu的密集性程序比较友好,但是依然,没有解决GIL,导致的同一时间只能执行一个线程的问题,
所以效率还是很低的,
4.每个进程都有自己的独立的GIL,互不干扰,这样可以真正意义上的并行执行,所以在python下面
多进程的效率优于多线程。
我们得出的结论,就是想要提升效率,比较通用的方法就是使用多进程。能够有效的提升效率
基于上面的原因,python是按多进程是比较号的选择。
由于GIL的存在,python中多线程其实并不是真正的多线程,如果想要充分的
利用多核cpu资源,在python应该使用多进程。python提供了非常友好的
多进程包。
multiprocessing只需要定义一个函数,python会完成其他所有的事情
借助这个包,可以完成单进程到并发执行的转换。multiprocessing支持
子进程,通讯和共享数据。执行不同形式的同步。提供了process,Queue,pipe
lock等等组件,
multiprocessing包是多进程的管理包,与threading.Thread类似,可以利用
multiprocessing.process来创建一个进程。该进程可以运行在python的内部
编写的函数里面。该process对象和thread对象的用法是相同的,也有start,run,
join方法,此外multiprocessing包中也有Lock,Event,Semaphore/Condiction类
这些对象可以像多线程通过参数传递给多线程)可以同步进程,用法与threading同名类一致。
所以multiprocessing的很大一部分与threading使用同一套API,只不过换到了多进程的
情景。
在使用这些共享的Api的时候,我们要注意的就是:
1.在unix平台上面,当某个进程终结之后,该进程需要被父进程调用wait,否则该进程
就会成为僵尸进程。所以有必要对每个进程调用join方法。实际上等同了wait,对于多线程
来说,由于只有一个进程,所以不存在次必要性
2.multiprocessing提供了threading包没有的IPC,比如(pipe和queue)效率更高。
英爱优先考虑使用他们,避免使用Lock,Event,Semaphore/Condiction等同步的方式
3,多进程英爱避免共享资源,我们可以比较容易的共享资源。比如使用全局变量或者传递参数
在多进程里面,由于每一个进程有自己独立的内存空间,以上方法并不适合
我们必须通过共享内存和manager方法来共享资源,但是这样做提高程序的复杂度
同步额需要降低了程序的效率
process。PID中保存有PID,如果进程中没有start方法,那么PID为none
在windows情况下,需要注意的就是想要启动一个子进程,必须加上;
if __name=="__main__"
进程相关要写在上面者语句的下面。
还是来看看代码吧。
from multiprocessing import Process
import time
def f(name):
if __name__=="__main__":
#coding=utf-8
'''
进程的类是调用,还是来看看代码吧。
'''
from multiprocessing import Process
import time
class MyProcess(Process):
if __name__=="__main__":
看看输出的结果:
来分下下输出的结果:
前一篇:python之间进程通讯
后一篇:进程的类式调用