Python进程-实现
fork介绍
Unix/Linux操作系统提供了一个fork()
系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()
调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。
子进程永远返回0
,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()
就可以拿到父进程的ID。
Python的os
模块封装了常见的系统调用
import os if __name__ == '__main__': print('进程 (%s) start...' % os.getpid()) pid = os.fork() # time.sleep(10) if pid == 0: # 子进程fork()返回0 print("子进程{},父进程{}".format(os.getpid(), os.getppid())) else: # 父进程fork返回子进程的id print("父进程{},子进程{}".format(os.getpid(), pid)) --》》结果 进程 (3130) start... 父进程3130,子进程3131 子进程3131,父进程3130
multiprocessing模块介绍
python中的多线程无法利用CPU资源,在python中大部分计算密集型任务使用多进程。如果想要充分地使用多核CPU的资源(os.cpu_count()查看)
python中提供了非常好的多进程包multiprocessing。
multiprocessing模块用来开启子进程,并在子进程中执行功能(函数),该模块与多线程模块threading的编程接口类似。
multiprocessing的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
计算密集型任务:如金融分析,科学计算
注意
记住,进程的执行是有操作系统调度的,python执行进程(start)只是向操作系统发信号,告诉操作系统,我要开辟一个进程,由操作系统调度执行。
同理,python终止进程(terminate)只是向操作系统发信号,告诉操作系统,我要终止一个进程,而不会马上终止,由操作系统调度终止进程。
Process类的介绍
1.创建进程的类
Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动) 强调: 1. 需要使用关键字的方式来指定参数 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
2.参数介绍
group参数未使用,值始终为None target表示调用对象,即子进程要执行的任务 args表示调用对象的位置参数元组,args=(1,2,'al',) kwargs表示调用对象的字典,kwargs={'name':'al','age':18} name为子进程的名称
3.方法介绍
p.start():启动进程,并调用该子进程中的p.run()
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。
如果p还保存了一个锁那么也将不会被释放,进而导致死锁
p.is_alive():如果p仍然运行,返回True
p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,
需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
4.属性介绍
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
p.name:进程的名称
p.pid:进程的pid
p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,
这类连接只有在具有相同的身份验证键时才能成功(了解即可)
Process类的使用
注意:在windows中Process()必须放到# if __name__ == '__main__':下
开一个进程和主进程是并发的关系,我start一下就是先告诉操作系统我要开一个进程
,然而它不会等待,他会去执行下面的代码,完了他把进程开始后,就开始执行了
# window创造进程相当于导入,会无线递归创建 # Linux 是把父进程拷贝到子进程 import multiprocessing import time import threading print('main process start1') def func(): print('subprocess start',time.asctime(time.localtime(time.time()))) time.sleep(3) print('subprocess end',time.asctime(time.localtime(time.time()))) print('main process start2') p = multiprocessing.Process(target=func, name='1号') p.start() print('main process end', time.asctime(time.localtime(time.time())),multiprocessing.current_process()) if __name__ == '__main__': pass # 默认所有进程结束后,程序才结束 """ linux main process start1 main process start2 main process end Fri Mar 23 00:20:29 2018 <_MainProcess(MainProcess, started)> subprocess start Fri Mar 23 00:20:29 2018 subprocess end Fri Mar 23 00:20:32 2018 """ """ windows Make sure that the main module can be safely imported by a new Python interpreter without causing unintended side effects (such a starting a new process)." main process start1 main process start2 main process end Fri Mar 23 00:23:50 2018 <_MainProcess(MainProcess, started)> main process start1 #相当于导入一次该程序,在main函数之前调用进程,会造成无限循环输出导致报错 subprocess start Fri Mar 23 00:23:50 2018 subprocess end Fri Mar 23 00:23:53 2018 """
strat():方法的功能
1.开启进程
2.执行功能
开启进程方法一
import time import multiprocessing # import threading def func(): i = 0 for _ in range(10000000): i += 1 return True def main(): start = time.time() p = multiprocessing.Process(target=func) # 创建一个子进程 p1 = multiprocessing.Process(target=func) p.start() p1.start() # p = threading.Thread(target=func) # p.start() func() end = time.time() print(end - start) if __name__ == '__main__': main()
开启进程方法二
from multiprocessing import Process import time import random import os class pro(Process): def __init__(self,name): super().__init__() # 必须继承父类的一些属性 self.name = name def run(self): # 必须得实现一个run方法 print('%s is work,父进程%s,当前进程%s'%(self.name,os.getppid(),os.getpid())) time.sleep(1) print('%s is work end'%self.name) if __name__ =='__main__': p1 = pro('al') p2 = pro('b1') p3 = pro('c1') p1.start() p2.start() p3.start() print('主进程',os.getpid()) -》》输出 主进程 9116 b1 is work,父进程9116,当前进程4316 al is work,父进程9116,当前进程6664 c1 is work,父进程9116,当前进程10132 b1 is work end al is work end c1 is work end
使用daemon
- 守护进程会在主进程代码执行结束后就终止
from multiprocessing import Process import time import random import os class My_Process(Process): def __init__(self,name): super().__init__() # 必须继承父类的一些属性 self.name = name def run(self): # 必须得实现一个run方法 time.sleep(5) print('%s is work end'%self.name) if __name__ =='__main__': start_time = time.time() print("process start") for i in range(5): t = My_Process('Process%s' % i) # t.daemon = True t.start() time.sleep(3) print("process end") print(time.time() - start_time) """ t.daemon = False process start process end #主进程运行完毕后,程序等待子进程运行完成后结束 0.020070791244506836 Process0 is work end Process2 is work end Process3 is work end Process4 is work end Process1 is work end Process finished with exit code 0 """ """ t.daemon = True process start process end #主进程运行完毕后,守护进程结束 3.014104127883911 Process finished with exit code 0 """
2. 守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
from multiprocessing import Process import time import random def task(name): print('%s is start' %name) time.sleep(random.randrange(1,3)) print('%s is end' %name) p = Process(target=time.sleep, args=(3,)) #在守护进程里面开子进程报错 p.start() if __name__ == '__main__': p=Process(target=task,args=('egon',)) p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行 p.start() p.join() print('主') #只要终端打印出这一行内容,那么守护进程p也就跟着结束掉了 ----------- egon is start egon is end Process Process-1: Traceback (most recent call last): File "C:\Program Files\Python36\lib\multiprocessing\process.py", line 249, in _bootstrap self.run() File "C:\Program Files\Python36\lib\multiprocessing\process.py", line 93, in run self._target(*self._args, **self._kwargs) File "C:\Users\jingjing\PycharmProjects\py3Project\路飞\第四模块课件\01网络编程进阶\01 多进程\05 守护进程.py", line 55, in task p.start() File "C:\Program Files\Python36\lib\multiprocessing\process.py", line 103, in start 'daemonic processes are not allowed to have children' AssertionError: daemonic processes are not allowed to have children 主
思考下列代码的执行结果有可能有哪些情况?为什么?
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-------") -----输出 main------- 456 end456 当输出main之后,守护进程就结束了
使用terminate
import multiprocessing import time import threading def func(): print('subprocess start',time.asctime(time.localtime(time.time()))) time.sleep(3) print('subprocess end',time.asctime(time.localtime(time.time()))) if __name__ == '__main__': p = multiprocessing.Process(target=func, name='1号') p.start() p.terminate() # 终止进程 time.sleep(5) print(p.is_alive()) # 判断这个进程是否存在 # 默认所有进程结束后,程序才结束 print('main process end', time.asctime(time.localtime(time.time())), multiprocessing.current_process())
-》》》输出 """ False 终止进程后is_alive()返回False,没有执行print('subprocess s…… main process end Fri Mar 23 17:22:57 2018 <_MainProcess(MainProcess, started)> """
使用join
import multiprocessing import time import threading def func(): print('subprocess start',time.asctime(time.localtime(time.time()))) time.sleep(3) print('subprocess end',time.asctime(time.localtime(time.time()))) if __name__ == '__main__': p = multiprocessing.Process(target=func, name='1号') p.start() p.join() # 等待子进程完成后,结束主线程 # 默认所有进程结束后,程序才结束 print('main process end', time.asctime(time.localtime(time.time())), multiprocessing.current_process()) """ 不使用join # 主线程调用子线程后直接结束,然后子线程再结束 main process end Fri Mar 23 17:34:01 2018 <_MainProcess(MainProcess, started)> subprocess start Fri Mar 23 17:34:01 2018 subprocess end Fri Mar 23 17:34:04 2018 """ """ 使用join subprocess start Fri Mar 23 17:30:20 2018 subprocess end Fri Mar 23 17:30:23 2018 # 等待子进程完成后,结束主线程 main process end Fri Mar 23 17:30:23 2018 <_MainProcess(MainProcess, started)> """
join用法考题
类似于单进程
from multiprocessing import Process import time def task(n, t): time.sleep(t) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,5)) p2=Process(target=task,args=(2,2)) p3=Process(target=task,args=(3,1)) start = time.time() p1.start() p1.join() p2.start() p2.join() p3.start() p3.join() print('-------->time', time.time() - start) ------------------输出-----相当于单进程,时间等于3个进程执行时间的相加 -------->1 -------->2 -------->3 -------->time 9.534545183181763
类似于多进程
from multiprocessing import Process import time def task(n, t): time.sleep(t) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,5)) p2=Process(target=task,args=(2,2)) p3=Process(target=task,args=(3,1)) start = time.time() p_l = [p1,p2,p3] for p in p_l: p.start() for p in p_l: p.join() print('--------4 >time', time.time() - start) ---------------输出------------相当于多进程,执行时间等于最长进程的执行时间 -------->3 -------->2 -------->1 --------4 >time 5.624321699142456
使用多进程实现并发的服务器
服务器端
import socket import multiprocessing server = socket.socket() server.bind(('127.0.0.1', 9988)) server.listen(5) def read_msg(conn, addr): while True: try: data = conn.recv(1024) if data: # 正常接收数据 print('收到{}:数据{}'.format(addr,data.decode())) conn.send(data) else: # 收到空消息,关闭 print('close:{}'.format(addr)) break except Exception as e: print(e) break conn.close() if __name__ == '__main__': while True: print('-------主进程,等待连接------') conn, addr = server.accept() print('创建一个新的进程,和客户端{}通信'.format(addr)) client= multiprocessing.Process(target=read_msg, args=(conn, addr)) # client = threading.Thread(target=readable, args=((conn, addr))) # p.start() client.start() server.close()
客户端
# 客户端 import socket # 导入socket模块 socket_client = socket.socket() # 创建监听套接字 socket_client.connect(('127.0.0.1', 9988)) # 连接套接字 ,要连接的IP与端口 while 1: cmd = input("Please input cmd:") # 与人交互,输入命令 if not cmd: continue elif cmd == 'q':break socket_client.send(cmd.encode('utf-8')) # 把命令发送给服务端 data = socket_client.recv(1024) print('recv',data) socket_client.close()
练习题
from multiprocessing import Process n=100 #在windows系统中应该把全局变量定义在if __name__ == '__main__'之上就可以了 def work(): global n n=0 print('子进程内: ',n) if __name__ == '__main__': p=Process(target=work) p.start() print('主进程内: ',n) 进程之间的内存空间是共享的还是隔离的?上述代码的执行结果是什么? 进程之间的内存空间是隔离的 执行结果: 主进程内: 100 子进程内: 0
改写下列程序,分别别实现下述打印效果
from multiprocessing import Process import time import random def task(n): time.sleep(random.randint(1,3)) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2=Process(target=task,args=(2,)) p3=Process(target=task,args=(3,)) p1.start() p2.start() p3.start() print('-------->4')
效果一:保证最先输出-------->4
from multiprocessing import Process import time import random def task(n): time.sleep(random.randint(1,3)) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2=Process(target=task,args=(2,)) p3=Process(target=task,args=(3,)) p1.start() p2.start() p3.start() print('-------->4') ----------输出 -------->4 -------->1 -------->3 -------->2
效果二:保证最后输出-------->4
from multiprocessing import Process import time import random def task(n): time.sleep(random.randint(1,3)) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2=Process(target=task,args=(2,)) p3=Process(target=task,args=(3,)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print('-------->4') ----------------------------输出 -------->1 -------->3 -------->2 -------->4
效果三:保证按顺序输出
from multiprocessing import Process import time import random def task(n): time.sleep(random.randint(1,3)) print('-------->%s' %n) if __name__ == '__main__': p1=Process(target=task,args=(1,)) p2=Process(target=task,args=(2,)) p3=Process(target=task,args=(3,)) p1.start() p1.join() p2.start() p2.join() p3.start() p3.join() print('-------->4') -------------------------输出 -------->1 -------->2 -------->3 -------->4
2、判断上述三种效果,哪种属于并发,哪种属于串行?
第一种和第二种属于并发,最后一种属于并行