进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操做系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。python
第一,进程是一个实体。每个进程都有它本身的地址空间,通常状况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码; 数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。linux
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操做系统执行之),它才能成为一个活动的实体,咱们称其为进程。进程是操做系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态状况,描述系统内部各道程序的活动规律引进的一个概念,全部多道程序设计操做系统都创建在进程的基础上。nginx
从理论角度看,是对正在运行的程序过程的抽象;web
从实现角度看,是一种数据结构,目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。算法
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。shell
并发性:任何进程均可以同其余进程一块儿并发执行 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;数据库
异步性:因为进程间的相互制约,使进程具备执行的间断性,即进程按各自独立的、不可预知的速度向前推动编程
结构特征:进程由程序、数据和进程控制块三部分组成。 多个不一样的进程能够包含相同的程序:一个程序在不一样的数据集里就构成不一样的进程,能获得不一样的结果;json
可是执行过程当中,程序不能发生改变。windows
程序是指令和数据的有序集合,其自己没有任何运行的含义,是一个静态的概念。而进程是程序在处理机上的一次执行过程,它是一个动态的概念。
程序能够做为一种软件资料长期存在,而进程是有必定生命期的。
程序是永久的,进程是暂时的。
注意:同一个程序执行两次,就会在操做系统中出现两个进程,因此咱们能够同时运行一个软件,分别作不一样的事情也不会混乱。
要想多个进程交替运行,操做系统必须对这些进程进行调度,这个调度也不是随即进行的,而是须要遵循必定的法则,由此就有了进程的调度算法。
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于做业调度,也可用于进程调度。FCFS算法比较有利于长做业(进程),而不利于短做业(进程)。由此可知,本算法适合于CPU繁忙型做业,而不利于I/O繁忙型的做业(进程)。
短做业(进程)优先调度算法(SJ/PF)是指对短做业或短进程优先调度的算法,该算法既可用于做业调度,也可用于进程调度。但其对长做业不利;不能保证紧迫性做业(进程)被及时处理;做业的长短只是被估算出来的。
时间片轮转(Round Robin,RR)法的基本思路是让每一个进程在就绪队列中的等待时间与享受服务的时间成比例。在时间片轮转法中,须要将CPU的处理时间分红固定大小的时间片,例如,几十毫秒至几百毫秒。若是一个进程在被调度选中以后用完了系统规定的时间片,但又未完成要求的任务,则它自行释放本身所占有的CPU而排到就绪队列的末尾,等待下一次调度。同时,进程调度程序又去调度当前就绪队列中的第一个进程。 显然,轮转法只能用来调度分配一些能够抢占的资源。这些能够抢占的资源能够随时被剥夺,并且能够将它们再分配给别的进程。CPU是可抢占资源的一种。但打印机等资源是不可抢占的。因为做业调度是对除了CPU以外的全部系统硬件资源的分配,其中包含有不可抢占资源,因此做业调度不使用轮转法。 在轮转法中,时间片长度的选取很是重要。首先,时间片长度的选择会直接影响到系统的开销和响应时间。若是时间片长度太短,则调度程序抢占处理机的次数增多。这将使进程上下文切换次数也大大增长,从而加剧系统开销。反过来,若是时间片长度选择过长,例如,一个时间片能保证就绪队列中所需执行时间最长的进程能执行完毕,则轮转法变成了先来先服务法。时间片长度的选择是根据系统对响应时间的要求和就绪队列中所容许最大的进程数来肯定的。 在轮转法中,加入到就绪队列的进程有3种状况: 一种是分给它的时间片用完,但进程还未完成,回到就绪队列的末尾等待下次调度去继续执行。 另外一种状况是分给该进程的时间片并未用完,只是由于请求I/O或因为进程的互斥与同步关系而被阻塞。当阻塞解除以后再回到就绪队列。 第三种状况就是新建立进程进入就绪队列。 若是对这些进程区别对待,给予不一样的优先级和时间片从直观上看,能够进一步改善系统服务质量和效率。例如,咱们可把就绪队列按照进程到达就绪队列的类型和进程被阻塞时的阻塞缘由分红不一样的就绪队列,每一个队列按FCFS原则排列,各队列之间的进程享有不一样的优先级,但同一队列内优先级相同。这样,当一个进程在执行完它的时间片以后,或从睡眠中被唤醒以及被建立以后,将进入不一样的就绪队列。
前面介绍的各类用做进程调度的算法都有必定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,并且若是并未指明进程的长度,则短进程优先和基于进程长度的抢占式调度算法都将没法使用。 而多级反馈队列调度算法则没必要事先知道各类进程所需的执行时间,并且还能够知足各类类型进程的须要,于是它是目前被公认的一种较好的进程调度算法。在采用多级反馈队列调度算法的系统中,调度算法的实施过程以下所述。 (1) 应设置多个就绪队列,并为各个队列赋予不一样的优先级。第一个队列的优先级最高,第二个队列次之,其他各队列的优先权逐个下降。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每一个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。 (2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,即可准备撤离系统;若是它在一个时间片结束时还没有完成,调度程序便将该进程转入第二队列的末尾,再一样地按FCFS原则等待调度执行;若是它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长做业(进程)从第一队列依次降到第n队列后,在第n 队列便采起按时间片轮转的方式运行。 (3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。若是处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。
并行 : 并行是指二者同时执行,好比赛跑,两我的都在不停的往前跑;(资源够用,好比三个线程,四核的CPU )
并发 : 并发是指资源有限的状况下,二者交替轮流使用资源,好比一段路(单核CPU资源)同时只能过一我的,A走一段后,让给B,B用完继续给A ,交替使用,目的是提升效率。
区别:
并行是从微观上,也就是在一个精确的时间片刻,有不一样的程序在执行,这就要求必须有多个处理器。
并发是从宏观上,在一个时间段上能够看出是同时执行的,好比一个服务器同时处理多个session。
所谓同步就是一个任务的完成须要依赖另一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列
。要么成功都成功,失败都失败,两个任务的状态能够保持一致。
所谓异步是不须要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工做,依赖的任务也当即执行,只要本身完成了整个任务就算完成了
。至于被依赖的任务最终是否真正完成,依赖它的任务没法肯定,
因此它是不可靠的任务序列
。
好比我去银行办理业务,可能会有两种方式:
第一种 :选择排队等候;
第二种 :选择取一个小纸条上面有个人号码,等到排到我这一号时由柜台的人通知我轮到我去办理业务了;
第一种:前者(排队等候)就是同步等待消息通知,也就是我要一直在等待银行办理业务状况;
第二种:后者(等待别人通知)就是异步等待消息通知。在异步消息处理中,等待消息通知者(在这个例子中就是等待办理业务的人)每每注册一个回调机制,在所等待的事件被触发时由触发机制(在这里是柜台的人)经过某种机制(在这里是写在小纸条上的号码,喊号)找到等待该事件的人。
阻塞和非阻塞这两个概念与程序(线程)等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来讲的。
继续上面的那个例子,不管是排队仍是使用号码等待通知,若是在这个等待的过程当中,等待者除了等待消息通知以外不能作其它的事情,那么该机制就是阻塞的,表如今程序中,也就是该程序一直阻塞在该函数调用处不能继续往下执行。
相反,有的人喜欢在银行办理这些业务的时候一边打打电话发发短信一边等待,这样的状态就是非阻塞的,由于他(等待者)没有阻塞在这个消息通知上,而是一边作本身的事情一边等待。
注意:同步非阻塞形式其实是效率低下的,想象一下你一边打着电话一边还须要抬头看到底队伍排到你了没有。若是把打电话和观察排队的位置当作是程序的两个操做的话,这个程序须要在这两种不一样的行为之间来回的切换,效率可想而知是低下的;而异步非阻塞形式却没有这样的问题,由于打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不一样的操做中来回切换。
效率最低。拿上面的例子来讲,就是你专心排队,什么别的事都不作。
若是在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知)
,也就是领了一张小纸条,假如在这段时间里他不能离开银行作其它的事情,那么很显然,这我的被阻塞在了这个等待的操做上面;
异步操做是能够被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。
其实是效率低下的。
想象一下你一边打着电话一边还须要抬头看到底队伍排到你了没有,若是把打电话和观察排队的位置当作是程序的两个操做的话,这个程序须要在这两种不一样的行为之间来回的切换
,效率可想而知是低下的。
效率更高,
由于打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不一样的操做中来回切换
。
好比说,这我的忽然发觉本身烟瘾犯了,须要出去抽根烟,因而他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操做上面,天然这个就是异步+非阻塞的方式了。
不少人会把同步和阻塞混淆,是由于不少时候同步操做会以阻塞的形式表现出来
,一样的,不少人也会把异步和非阻塞混淆,由于异步操做通常都不会在真正的IO操做处被阻塞
。
但凡是硬件,都须要有操做系统去管理,只要有操做系统,就有进程的概念,就须要有建立进程的方式,一些操做系统只为一个应用程序设计,好比微波炉中的控制器,一旦启动微波炉,全部的进程都已经存在。
而对于通用系统(跑不少应用程序),须要有系统运行过程当中建立或撤销进程的能力,主要分为4中形式建立新的进程:
1. 系统初始化(查看进程linux中用ps命令,windows中用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台而且只在须要时才唤醒的进程,称为守护进程,如电子邮件、web页面、新闻、打印)
2. 一个进程在运行过程当中开启了子进程(如nginx开启多进程,os.fork,subprocess.Popen等)
3. 用户的交互式请求,而建立一个新进程(如用户双击暴风影音)
4. 一个批处理做业的初始化(只在大型机的批处理系统中应用)
不管哪种,新进程的建立都是由一个已经存在的进程执行了一个用于建立进程的系统调用而建立的。
1. 在UNIX中该系统调用是:fork,fork会建立一个与父进程如出一辙的副本,两者有相同的存储映像、一样的环境字符串和一样的打开文件(在shell解释器进程中,执行一个命令就会建立一个子进程) 2. 在windows中该系统调用是:CreateProcess,CreateProcess既处理进程的建立,也负责把正确的程序装入新进程。 关于建立子进程,UNIX和windows 1.相同的是:进程建立后,父进程和子进程有各自不一样的地址空间(多道技术要求物理层面实现进程之间内存的隔离),任何一个进程的在其地址空间中的修改都不会影响到另一个进程。 2.不一样的是:在UNIX中,子进程的初始地址空间是父进程的一个副本,提示:子进程和父进程是能够有只读的共享内存区的。可是对于windows系统来讲,从一开始父进程与子进程的地址空间就是不一样的。
1. 正常退出(自愿,如用户点击交互式页面的叉号,或程序执行完毕调用发起系统调用正常退出,在linux中用exit,在windows中用ExitProcess)
2. 出错退出(自愿,python a.py中a.py不存在)
3. 严重错误(非自愿,执行非法指令,如引用不存在的内存,1/0等,能够捕捉异常,try...except...)
4. 被其余进程杀死(非自愿,如kill -9)
以前咱们已经了解了不少进程相关的理论知识,了解进程是什么应该再也不困难了,刚刚咱们已经了解了,运行中的程序就是一个进程。全部的进程都是经过它的父进程来建立的。所以,运行起来的python程序也是一个进程,那么咱们也能够在程序中再建立进程。多个进程能够实现并发效果,也就是说,当咱们的程序中存在多个进程的时候,在某些时候,就会让程序的执行速度变快。以咱们以前所学的知识,并不能实现建立进程这个功能,因此咱们就须要借助python中强大的模块。
仔细说来,multiprocess不是一个模块而是python中一个操做、管理进程的包。 之因此叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的全部子模块。因为提供的子模块很是多,为了方便你们归类记忆,我将这部分大体分为四个部分:建立进程部分,进程同步部分,进程池部分,进程之间数据共享。
process模块是一个建立进程的模块,借助这个模块,就能够完成进程的建立
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化获得的对象,表示一个子进程中的任务(还没有启动) 强调: 1. 须要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号 参数介绍: 1 group参数未使用,值始终为None 2 target表示调用对象,即子进程要执行的任务 3 args表示调用对象的位置参数元组,args=(1,2,'egon',) 4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} 5 name为子进程的名称
1 p.start():启动进程,并调用该子进程中的p.run() 2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,咱们自定义类的类中必定要实现该方法 3 p.terminate():强制终止进程p,不会进行任何清理操做,若是p建立了子进程,该子进程就成了僵尸进程,使用该方法须要特别当心这种状况。若是p还保存了一个锁那么也将不会被释放,进而致使死锁 4 p.is_alive():若是p仍然运行,返回True 5 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,须要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
1 p.daemon:默认值为False,若是设为True,表明p为后台运行的守护进程,当p的父进程终止时,p也随之终止,而且设定为True后,p不能建立本身的新进程,必须在p.start()以前设置 2 p.name:进程的名称 3 p.pid:进程的pid 4 p.exitcode:进程在运行时为None、若是为–N,表示被信号N结束(了解便可) 5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络链接的底层进程间通讯提供安全性,这类链接只有在具备相同的身份验证键时才能成功(了解便可)
在Windows操做系统中因为没有fork(linux操做系统中建立进程的机制),在建立子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。所以若是将process()直接写在文件中就会无限递归建立子进程报错。因此必须把建立子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候 ,就不会递归运行了。
在一个python进程中开启子进程,start方法和并发效果。
def func(): print('个人pid:%s,父进程的pid:%s'%(os.getpid(),os.getppid())) if __name__ =='__main__': print(os.getpid(),os.getppid()) p = Process(target=func) p.start()
import os from multiprocessing import Process def func(): print('个人pid:%s,父进程的pid:%s'%(os.getpid(),os.getppid())) if __name__ =='__main__': p = Process(target=func) p.start() p.join() print('个人pid:%s,父进程的pid:%s'%(os.getpid(), os.getppid()))
进阶,多个进程同时运行(注意,子进程的执行顺序不是根据启动顺序决定的)
from multiprocessing import Process import time def func(num): print('%s<>hello'%num) time.sleep(0.5) if __name__ == '__main__': p_list = [] for i in range(10): p = Process(target=func,args=(i,)) p.start() p_list.append(p) p.join() print('主进程')
from multiprocessing import Process import time def func(num): print('%s<>hello'%num) time.sleep(0.5) if __name__ == '__main__': p_list = [] for i in range(10): p = Process(target=func,args=(i,)) p.start() p_list.append(p) [p.join() for p in p_list] print('主进程')
很明显方法二的执行效率更高,符合并发
除了上面这些开启进程的方法,还有一种以继承Process类的形式开启进程的方式
from multiprocessing import Process class Myprocess(Process): def __init__(self, name): super().__init__() self.name = name def run(self): print('in 子进程') print('hello:%s' % self.name) if __name__ == '__main__': p = Myprocess('wangjifei') p.start() p.join() print('in 主进程')
import os from multiprocessing import Process import time n = 1 def func(): global n n = 100 print('个人pid:%s,父进程的pid:%s'%(os.getpid(),os.getppid())) if __name__ =='__main__': print(os.getpid(),os.getppid()) p = Process(target=func) p.start() time.sleep(1) print(n)
经过建立子进程实现socket多人聊天功能
import socket from multiprocessing import Process def chat(conn): conn.send(b'hello') print(conn.recv(1024).decode('utf-8')) conn.close() sk.close() if __name__ == '__main__': sk = socket.socket() sk.bind(('127.0.0.1', 8000)) sk.listen() while True: conn, addr = sk.accept() p = Process(target=chat, args=(conn,)) p.start()
import socket sk = socket.socket() sk.connect(('127.0.0.1', 8000)) print(sk.recv(1024)) msg = input('>>>:') sk.send(msg.encode('utf-8')) sk.close()
会随着主进程的结束而结束。
主进程建立守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内没法再开启子进程,不然抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
import time from multiprocessing import Process def func1(): while True: print('is alive') time.sleep(0.5) if __name__ == "__main__": p = Process(target=func1) p.daemon = True #守护进程参数,这就成为了守护进程 p.start() time.sleep(3) print('主进程')
import time from multiprocessing import Process def func1(): while True: print('is alive') time.sleep(0.5) def func2(): for i in range(10): print('第%s秒' % i) time.sleep(1) if __name__ == "__main__": Process(target=func2).start() p = Process(target=func1) p.daemon = True # 守护进程参数,这就成为了守护进程 p.start() time.sleep(3) print('主进程')
import time from multiprocessing import Process def func1(): while True: print('is alive') time.sleep(0.5) if __name__ == "__main__": p = Process(target=func1) p.daemon = True print(p.is_alive()) #守护进程参数,这就成为了守护进程 p.start() time.sleep(1) print(p.is_alive()) # 判断进程是否存活 p.terminate() # 杀死进程 time.sleep(3) print('主进程') print(p.is_alive()) # 判断进程是否存活
from multiprocessing import Process import time import random class Myprocess(Process): def __init__(self,person): self.name=person # name属性是Process中的属性,标示进程的名字 super().__init__() # 执行父类的初始化方法会覆盖name属性 # self.name = person # 在这里设置就能够修改进程名字了 self.person = person #若是不想覆盖进程名,就修改属性名称就能够了 def run(self): print('%s正在和网红脸聊天' %self.name) # print('%s正在和网红脸聊天' %self.person) time.sleep(random.randrange(1,5)) print('%s正在和网红脸聊天' %self.name) # print('%s正在和网红脸聊天' %self.person) if __name__ == '__main__': p1=Myprocess('哪吒') p1.start() print(p1.pid) #能够查看子进程的进程id
经过刚刚的学习,咱们想方设法实现了程序的异步,让多个任务能够同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受咱们控制。
尽管并发编程让咱们能更加充分的利用IO资源,可是也给咱们带来了新的问题。
当多个进程使用同一份数据资源的时候,就会引起数据安全或顺序混乱问题。
from multiprocessing import Lock lock = Lock() lock.acquire() # 想拿钥匙 print(1) lock.release() # 还钥匙 lock.acquire() # 想拿钥匙 print('拿到钥匙了') lock.release() # 还钥匙
import json import time from multiprocessing import Process,Lock def search(i): with open('db') as f:ticket_dic = json.load(f) print('%s正在查票,剩余票数 :%s'%(i,ticket_dic['count'])) def buy(i): with open('db') as f:ticket_dic = json.load(f) time.sleep(0.2) # 模拟请求数据库的网络延时 if ticket_dic['count'] > 0: ticket_dic['count'] -= 1 print('%s买到票了'%i) time.sleep(0.2) # 模拟往数据库写消息的网络延迟 with open('db','w') as f:json.dump(ticket_dic,f) def get_ticket(i,lock): search(i) # 查询余票,能够多个用户同时查询 with lock : buy(i) # 买票的操做涉及到数据修改,须要加锁来保证数据的安全 # def get_ticket(i,lock): # search(i) # 查询余票,能够多个用户同时查询 # lock.acquire() # buy(i) # 买票的操做涉及到数据修改,须要加锁来保证数据的安全 # lock.release() if __name__ == '__main__': lock = Lock() for i in range(10): p = Process(target=get_ticket,args = (i,lock)) p.start()
锁的做用: 维护数据的安全
下降了程序的效率
全部的效率都是创建在数据安全的角度上的
但凡涉及到并发编程都要考虑数据的安全性
咱们须要在并发部分对数据修改的操做格外当心,若是会涉及到数据的不安全,就须要进行加锁控制
#加锁能够保证多个进程修改同一块数据时,同一时间只能有一个任务能够进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 虽然能够用文件共享数据实现进程间通讯,但问题是: 1.效率低(共享数据基于文件,而文件是硬盘上的数据) 2.须要本身加锁处理 #所以咱们最好找寻一种解决方案可以兼顾:一、效率高(多个进程共享一块内存的数据)二、帮咱们处理好锁问题。这就是mutiprocessing模块为咱们提供的基于消息的IPC通讯机制:队列和管道。 队列和管道都是将数据存放于内存中 队列又是基于(管道+锁)实现的,可让咱们从复杂的锁问题中解脱出来, 咱们应该尽可能避免使用共享数据,尽量使用消息传递和队列,避免处理复杂的同步和锁问题,并且在进程数目增多时,每每能够得到更好的可获展性。
互斥锁同时只容许一个线程更改数据,而信号量Semaphore是同时容许必定数量的线程更改数据 。
假设商场里有4个迷你唱吧,因此同时能够进去4我的,若是来了第五我的就要在外面等待,等到有人出来才能再进去玩。
实现:
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,
acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,可是要区分开,信号量涉及到加锁的概念
# 信号量 至关于 锁 + 计数器 from multiprocessing import Semaphore sem = Semaphore(4) sem.acquire() print(1) sem.acquire() print(2) sem.acquire() print(3) sem.acquire() print(4) sem.release() print(5)
import time import random from multiprocessing import Process,Semaphore # def ktv(sem,i): # sem.acquire() # print('%s走进KTV'%i) # time.sleep(random.randint(1,5)) # print('%s走出KTV'%i) # sem.release() def ktv(sem,i): with sem: print('%s走进KTV'%i) time.sleep(random.randint(1,5)) print('%s走出KTV'%i) if __name__ == '__main__': sem = Semaphore(4) for i in range(10): p = Process(target=ktv,args=(sem,i)) p.start()
python线程的事件用于主线程控制其余线程的执行,事件主要提供了三个方法 set、wait、clear。
事件处理的机制:全局定义了一个“Flag”,若是“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,若是“Flag”值为True,那么event.wait 方法时便再也不阻塞。
clear:将“Flag”设置为False
set:将“Flag”设置为True
# wait方法 # 在事件中有一个标志 # 若是这个标志是True, wait方法的执行效果就是pass # 若是这个标志是False,wait方法的效果就是阻塞 # 直到这个标志变成True # 控制标志 # 判断标志的状态 is_set # set方法 将标志设置为True # clear方法 将标志设置为False
import time from multiprocessing import Event,Process def func1(e): print('start func1') e.wait(1) print(e.is_set()) print('end func1') if __name__ == '__main__': e = Event() Process(target=func1,args=(e,)).start() time.sleep(5) e.set()
import time import random from multiprocessing import Process,Event def traffic_light(e): print('\033[1;31m红灯亮\033[0m') while True: time.sleep(2) if e.is_set(): print('\033[1;31m红灯亮\033[0m') e.clear() else: print('\033[1;32m绿灯亮\033[0m') e.set() def car(i,e): if not e.is_set(): print('car%s正在等在经过'%i) e.wait() print('car%s经过'%i) if __name__ == '__main__': e = Event() light = Process(target=traffic_light,args=(e,)) light.daemon = True light.start() car_lst = [] for i in range(20): p = Process(target=car,args=(i,e)) p.start() time.sleep(random.randint(0,3)) car_lst.append(p) for car in car_lst:car.join()