首先明白几个概念:
同步:做完一件事情,再做另外一件事情
异步:做一件事情的时候,可以再做另外一件事情
阻塞:recv sleep accept input recvfrom
非阻塞:没有遇见上面这些阻塞的情况就是非阻塞
阻塞与非阻塞这两个概念与程序等待消息通知(无所谓同步或者异步)时的状态有关,
也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的
并行:是指两者同时执行,比如多个cpu的情况,微观角度,也就是在一个精确的时间片刻,有不同的程序在执行
并发:是指资源有限的情况下,两者交替轮流使用资源,宏观角度,在一个时间段上可以看出是同时执行的,比如一个服务器同时处理多个session
进程三状态:
(1)就绪(Ready)状态
当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
(2)执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。
(3)阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,
例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。
什么是进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
multiprocess模块
从字面意思理解就是:多进程模块,具体来讲是python中一个操作、管理进程的包
process模块
process模块是一个创建进程的模块,借助这个模块,可以完成进程的创建
import os ,time from multiprocessing import Process def process1(): print('process1:',os.getppid()) time.sleep(1) print(os.getppid()) if __name__ == '__main__': #这里不写会报错,在mac系统和linux系统不写可以 # 在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 # import 启动它的这个文件,而在 # import 的时候又执行了整个文件。因此如果将process() # 直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if # __name__ ==‘__main__’ 判断保护起来,import 的时候 ,就不会递归运行了。 p=Process(target=process1) #调用函数 p.start() #启动函数
给process传参数
import os import time from multiprocessing import Process def process1(n,name): print('process1:',os.getppid()) print('n:',n,name) time.sleep(1) if __name__ == '__main__': print(os.getppid()) # 获取当前进程id p = Process(target=process1,args=[1,'alex']) # 调用函数并传参 p.start() # 启动进程
使用说明:
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
参数介绍:
1 group参数未使用,值始终为None 2 3 target表示调用对象,即子进程要执行的任务 4 5 args表示调用对象的位置参数元组,args=(1,2,'egon',) 6 7 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18} 8 9 name为子进程的名称
方法介绍:
1 p.start():启动进程,并调用该子进程中的p.run() 2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法 3 4 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁 5 p.is_alive():如果p仍然运行,返回True 6 7 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 3 p.name:进程的名称 4 5 p.pid:进程的pid 6 7 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可) 8 9 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
创建并开启子进程的两种方式:
方法一:以函数的方式
import time import random from multiprocessing import Process def piao(name): print('%s piaoing' %name) time.sleep(random.randrange(1,5)) print('%s piao end' %name) p1=Process(target=piao,args=('egon',)) #必须加,号 p2=Process(target=piao,args=('alex',)) p3=Process(target=piao,args=('wupeqi',)) p4=Process(target=piao,args=('yuanhao',)) p1.start() p2.start() p3.start() p4.start() print('主线程')
方法二:以面向对象的方式
# import os # import time # from multiprocessing import Process # class MyProcess(Process): # def __init__(self,arg): #只能在这里传参数 # super().__init__() #调用父类的init # self.arg=arg # def run(self): #在子进程中之间执行的 # print(self.arg) # time.sleep(1) # print('in run',os.getpid()) # # if __name__ == '__main__': # print('main: ',os.getpid()) # p=MyProcess(123) # p.start() # print(p.is_alive()) #True # p.terminate() #非阻塞,进程来不及停止 # print(p.is_alive()) #True # time.sleep(0.01) # print(p.is_alive()) #False
下面可以使用Process多进程实现socket server的效果:
服务端: import socket from multiprocessing import Process def talk(conn): while True: conn.send(b'hello') print(conn.recv(1024)) if __name__ == '__main__': sk=socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while True: conn,addr=sk.accept() Process(target=talk,args=(conn,)).start() 客户端:可以启动多个这样的客户端,实现了多并发 import socket client=socket.socket() client.connect(('127.0.0.1',8080)) while True: print(client.recv(1024)) client.send(b'byebye')
主进程和子进程的关系
1 主进程等待子进程结束才会结束 ---》 主进程需要回收子进程的资源
# import os # import time # from multiprocessing import Process # def func(): # print('in func before', os.getpid()) # time.sleep(10) # print('in func after',os.getpid()) # if __name__ == '__main__': # Process(target=func).start() # print('主进程',os.getpid()) # # raise ValueError #主动抛出异常 等待子进程结束主进程才会结束 # exit() #同样需要等待子进程结束而结束
2 下面是主进程出异常,立刻终止,并处理的情况,不等待子进程,直接强制退出,最后抛出异常
# import os # import time # from multiprocessing import Process # def func(): # print('in func before', os.getpid()) # time.sleep(10) # print('in func after',os.getpid()) # if __name__ == '__main__': # p=Process(target=func) # p.start() # print('主进程',os.getpid()) # try: # raise ValueError('111') # except Exception as e : # print(e) # p.terminate() #主进程强制结束,不会等待子进程结束 # print('after') # raise #最后抛出异常
3 开启多个子进程,它们之间是异步的,先后顺序和系统的cpu个数有关 ,os.cpu_count() -->查看cpu个数
# import os # import time # from multiprocessing import Process # n=100 # def func(): # global n # n-=1 # print('子进程',os.getpid(),os.getppid()) # if __name__ == '__main__': # print(os.getpid(),os.getppid()) # for i in range(9): #自己的电脑是4核超线程,等同于8个cpu,8核以及8核以内,主进程始终在子进程之前执行,8核以后看系统的情况 # p=Process(target=func) # p.start() # print('主进程') #主进程和子进程是异步的 # print('--->',n) #主进程和子进程是异步的 ''' 15760 14880 子进程 2676 15760 主进程 ---> 100 子进程 12252 15760 子进程 11712 15760 子进程 7012 15760 子进程 19004 15760 子进程 14300 15760 子进程 13360 15760 子进程 10020 15760 子进程 20216 15760 '''
4 使用join的方式,让子进程先于主进程进行 ,这里的先于是先于主进程在子进程启动后面的代码
# import os # import time # from multiprocessing import Process # n=100 # def func(): # global n # n-=1 # print('子进程',os.getpid(),os.getppid()) # if __name__ == '__main__': # print(os.getpid(),os.getppid()) # # p.join() #加上join后,子进程就优先于主进程执行 # p_lst=[] # for i in range(16): # p = Process(target=func) # p.start() # p_lst.append(p) # for p in p_lst:p.join() # print('主进程') # print('--->',n) ''' 3464 14880 子进程 6800 3464 子进程 16904 3464 子进程 18740 3464 子进程 8932 3464 子进程 15636 3464 子进程 9148 3464 子进程 16760 3464 子进程 5212 3464 子进程 10036 3464 子进程 5576 3464 子进程 10768 3464 子进程 8020 3464 子进程 10660 3464 子进程 8244 3464 子进程 16700 3464 子进程 11072 3464 主进程 ---> 100 '''
说明:以上的几个例子,n的值始终没有变,所以 进程之间的资源,数据都是隔离的
5 daemon 守护进程,将子进程设置为主进程的守护进程,主进程结束,子进程没结束立即停止
import os import time from multiprocessing import Process def func(): print('in func before',os.getpid()) time.sleep(3) print('in func after',os.getpid()) def func2(): print('in func2 before',os.getpid()) time.sleep(5) print('in func2 after',os.getpid()) if __name__ == '__main__': p=Process(target=func) p.daemon=True #设置为一个守护进程,将子进程设置为主进程的守护进程,在start之前设置 p.start() p=Process(target=func2) #func2能正常打印 p.start() time.sleep(1) print('主进程') #主进程的代码执行完毕之后,守护进程就自动结束了 ''' in func before 16264 in func2 before 18400 主进程 in func2 after 18400 '''
6 daemon守护进程的一个迷惑例子:主进程会等待子进程停止而停止,但是不会等待守护进程
from multiprocessing import Process import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") if __name__ == '__main__': p1=Process(target=foo) p2=Process(target=bar) p1.daemon=True p1.start() p2.start() print("main-------") #这行打印,主进程结束了,foo作为守护子进程也结束了,但是主进程要等待bar子进程 ''' main------- 456 end456 '''
7 进程的同步锁 Lock,这样一来可以一个一个打印,lock.acquire() 取得锁,lock.release()释放锁
from multiprocessing import Process,Lock import os,time def work(lock): lock.acquire() print('%s is running' % os.getpid()) time.sleep(2) print('%s is done'%os.getpid()) lock.release() if __name__ == '__main__': lock=Lock() for i in range(3): p=Process(target=work,args=(lock,)) p.start() 12472 is running 12472 is done 17416 is running 17416 is done 20460 is running 20460 is done
下面是一个模拟抢票例子
#文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db.txt')) print('