文章目录
- 一、多进程调用
- 二、Process类
- 1、主要参数
- 2、实例方法
- 3、属性
- 4、代码示例
- 三、进程通讯
- 1、进程队列通讯
- 2、管道通讯
- 3、Manager对象
- 四、进程同步
- 五、进程池
- 六、协程
- 1、协程简述
- 2、用greenlet库实现协程
- 3、用gevent库实现协程
一、多进程调用
与多线程调用相似
from multiprocessing import Process
import timedef f(name):time.sleep(1)print("hello",name,time.strftime("%X"))if __name__ == '__main__':print("start",time.strftime("%X"))p_list=[]for i in range(3):p = Process(target=f, args=("LuMX",))p_list.append(p)p.start()for i in p_list:i.join()print("end",time.strftime("%X"))
'''
start 15:09:57
hello LuMX 15:09:59
hello LuMX 15:09:59
hello LuMX 15:09:59
end 15:09:59
'''
from multiprocessing import Process
import timeclass MyProcess(Process):def run(self):time.sleep(1)print("hello",self.name,time.strftime("%X"))if __name__ == '__main__':print("start",time.strftime("%X"))for i in range(3):p=MyProcess()p.daemon=Truep.start()print("end",time.strftime("%X"))'''
start 15:26:31
end 15:26:31
'''
from multiprocessing import Process
import os, timedef info(title):print("title:", title)print("parent process:", os.getppid()) # 父进程的IDprint("process id", os.getpid())if __name__ == '__main__':info("main process line")time.sleep(1)print("----------------")p = Process(target=info,args=("LuMX",))p.start()p.join()'''
title: main process line
parent process: 9660
process id 12900
----------------
title: LuMX
parent process: 12900
process id 14000
'''
二、Process类
1、主要参数
target:要执行的方法
name:进程名
args/kwargs:要传入方法的参数
2、实例方法
is_alive():返回进程是否在运行
join([timeout]):阻塞当前环境的进程,直到调用此方法的进程终止或到达指定的timeout。
start():进程准备就绪,等待CPU调度
run():start()调用run方法,如果实例化进程时未指定传入target,则start默认执行run方法
terminate():不管任务是否完成,立即停止工作进程。
3、属性
daemon:和线程的setDaemon功能一样
name:进程名字
pid:进程号
4、代码示例
import time
from multiprocessing import Processclass MyProcess(Process):def __init__(self,num,name):super(MyProcess,self).__init__(name=name)self.num=numdef run(self):time.sleep(1)print(f"name:{self.name} pid:{self.pid} alive:{self.is_alive()} num:{self.num}",time.strftime("%X"))time.sleep(10)if __name__ == '__main__':print("main process start",time.strftime("%X"))p_list=[]for i in range(4):p = MyProcess(i,f"L{i}")p_list.append(p)for p in p_list:p.start()for p in p_list:p.join(1)print("main process end",time.strftime("%X"))
三、进程通讯
1、进程队列通讯
import queue,time
import multiprocessing
def foo(q):time.sleep(1)print("son process",id(q))q.put(123)q.put("haha")if __name__ == '__main__':q=multiprocessing.Queue() # 进程队列p=multiprocessing.Process(target=foo,args=(q,))p.start()print("main process",id(q))print(q.get())print(q.get())'''
main process 2032743575504
son process 2174242762864
123
haha
'''
2、管道通讯
from multiprocessing import Process, Pipe
import timedef f(conn):time.sleep(3)conn.send([12, {"age": 18}, "hellow"])response=conn.recv()print("response",response)conn.close()print("ID2:",id(conn))if __name__ == '__main__':parent_conn, child_conn = Pipe() # 返回两个双向管道print("ID1",id(child_conn))p=Process(target=f,args=(child_conn,))p.start()print(parent_conn.recv())time.sleep(2)parent_conn.send("hellow,Son")p.join()
3、Manager对象
进程队列和管道均是进程间通讯,即进程A的数据信息发送给进程B。
Manager对象支持进程数据共享,即进程A修改数据后,进程B访问该数据,是修改后的结果。
Manager对象支持的数据类型有:list, dict, Namespace, Lock, RLock, Semaphore, Queue等。
from multiprocessing import Process,Managerdef f(d,l,n):d[n] = "1"d["2"] = 2l.append(n)if __name__ == '__main__':with Manager() as manager:d=manager.dict() # 设置一个字典对象为共享数据l=manager.list() # 设置一个列表对象为共享数据p_list=[]for i in range(5):p = Process(target=f,args=(d,l,i))p.start()p_list.append(p)for res in p_list:res.join()print(d) # {1: '1', '2': 2, 0: '1', 2: '1', 3: '1', 4: '1'}print(l) # [1, 0, 2, 3, 4]
四、进程同步
from multiprocessing import Process,Lock
import timedef f(l,i):l.acquire()time.sleep(1)print("hellow world %s" %i)l.release()if __name__ == '__main__':lock = Lock()for num in range(5):Process(target=f, args=(lock,num)).start()
在上述例子中,各个进程并发申请向屏幕打印内容。如果不进行进程同步,可能上一个进程还没打印完,下一个进程就开始打印,造成窜行。
五、进程池
有50个任务,每个任务执行时间1s。如果开50个进程同时干,对系统资源的开销太大。而只开1个进程干,又太浪费时间。而进程池就是一个折中的方法。
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程。如果进程池序列中没有可供使用的进程,程序就会等待。
进程池中有两个方法:
aplly: 同步接口
apply_async: 异步接口
from multiprocessing import Process,Pool
import time,osdef Foo(i):time.sleep(1)print(i,time.strftime("%X"))print("son",os.getpid())return "hello %s" %idef Bar(arg):print("arg:",arg) # args为Foo返回值print("Bar:",os.getpid())if __name__ == '__main__':pool = Pool(5) # 进程池中开5个进程同时干活print("main pid",os.getpid())for i in range(50):pool.apply_async(func=Foo,args=(i,),callback=Bar)# func:表示要执行的函数。# args:表示向执行函数传递的参数# callback:表示回调函数,就是func函数执行成功后再去执行的函数,它在主进程里运行# 下面两句代码顺序是固定的pool.close() # 进程池不再接受新的任务pool.join() # 等待所有任务执行完成print("end")
六、协程
1、协程简述
协程又称微线程,是非抢占式的,底层实现是用yield语句切换函数。协程主要解决的也是IO操作密集型问题。协程实际上就是一个线程在执行,所以省去线程切换的开销,也不需要多线程的锁机制。
用多进程+协程的方法,既可以充分利用多核,又充分发挥协程的高效率。
import timedef consumer(name):print("-->ready to eat baozi...")while True:new_baozi = yieldprint("[%s] is eating baozi %s" % (name, new_baozi))def producer():con.__next__()con2.__next__()n = 0while n<=10:time.sleep(1)print("producer is making baozi %s and %s" % (n, n + 1))con.send(n)con2.send(n + 1)n += 2print("-----------")if __name__ == '__main__':con = consumer("c1")con2 = consumer("c2")producer()
2、用greenlet库实现协程
from greenlet import greenletdef test1():print(12)gr2.switch()print(34)def test2():print(56)gr1.switch() # 切换到test1print(78)gr1.switch()# 返回两个greenlet对象,不需要再提前声明为生成器函数
gr1 = greenlet(test1)
gr2 = greenlet(test2)print(gr1)
gr2.switch() # 切换到test2'''
<greenlet.greenlet object at 0x000002417FDECC00 (otid=0x0000000000000000) pending>
56
12
78
34
'''
3、用gevent库实现协程
gevent库比greenlet库进一步简化操作,可以不用手动切换协程,IO阻塞时,自动完成切换。
import gevent
import requests,timestart = time.time()def f(url):print("GET: %s" %url)resp = requests.get(url) # 爬取网页数据data = resp.textprint("%d bytes received from %s" %(len(data), url))# joinall方法:等待一组Greenlet(协程)完成
gevent.joinall([# spawn方法作用是创建一个协程gevent.spawn(f, 'https://www.python.org/'),gevent.spawn(f, 'https://www.yahoo.com/'),gevent.spawn(f, 'https://www.baidu.com/'),gevent.spawn(f, 'https://www.sina.com.cn/'),
])print("cost time:", time.time()-start)