文章目录
一、多进程调用
与多线程调用相似
from multiprocessing import Process
import time
def 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 time
class 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=True
p.start()
print("end",time.strftime("%X"))
'''
start 15:26:31
end 15:26:31
'''
from multiprocessing import Process
import os, time
def info(title):
print("title:", title)
print("parent process:", os.getppid()) # 父进程的ID
print("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 Process
class MyProcess(Process):
def __init__(self,num,name):
super(MyProcess,self).__init__(name=name)
self.num=num
def 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 time
def 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,Manager
def f(d,l,n):
d[n] = "1"
d["2"] = 2
l.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 time
def 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,os
def Foo(i):
time.sleep(1)
print(i,time.strftime("%X"))
print("son",os.getpid())
return "hello %s" %i
def 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 time
def consumer(name):
print("-->ready to eat baozi...")
while True:
new_baozi = yield
print("[%s] is eating baozi %s" % (name, new_baozi))
def producer():
con.__next__()
con2.__next__()
n = 0
while n<=10:
time.sleep(1)
print("producer is making baozi %s and %s" % (n, n + 1))
con.send(n)
con2.send(n + 1)
n += 2
print("-----------")
if __name__ == '__main__':
con = consumer("c1")
con2 = consumer("c2")
producer()
2、用greenlet库实现协程
from greenlet import greenlet
def test1():
print(12)
gr2.switch()
print(34)
def test2():
print(56)
gr1.switch() # 切换到test1
print(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,time
start = time.time()
def f(url):
print("GET: %s" %url)
resp = requests.get(url) # 爬取网页数据
data = resp.text
print("%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)