在python中,I/O密集型任务可以用多线程的方式来实现(threading库);然而,对于计算密集型任务,由于python中全局锁GIL的存在,多线程并不能起到一个加速的作用。所以此时,一般使用多进程的方式实现(multiprocessing库)。
多线程 threading: 一个人有与异性聊天和看剧两件事要做。单线程的她可以看完剧再去聊天,但这样子可能就没人陪她聊天了「哼,发消息不回」。我们把她看成一个CPU核心,为她开起多线程——先看一会剧,偶尔看看新消息,在两件事(线程)间来回切换。多线程:单个CPU核心可以同时做几件事,不至于卡在某一步傻等着。
用处:爬取网站信息(爬虫),等待多个用户输入
threading因为python全局锁的关系,实际是只用了cpu的一个核参与计算,所以适合io密集型的任务,在io计算时候,cpu可以进行别的任务计算
多进程 processing: 一个人有很多砖需要搬,他领取手套、推车各种物资(向系统申请了资源)然后开始搬砖。然而他身边有很多人,我们让这些人去帮他!(一核有难,八核围观)。于是他们做了分工,砖很快就搬完了。多进程让多个CPU核心可以一起做事,不至于只有一人干活而其他人傻站着。
用处:进行高性能计算。只有多进程方案设计合理,才能加速计算。
multiprocessing库是实现了cpu的多核,实现并行计算,进行多进程
def function1(id): # 这里是子进程 print(f'id {id}') def run__process(): # 这里是主进程 from multiprocessing import Process process = [mp.Process(target=function1, args=(1,)), mp.Process(target=function1, args=(2,)), ] [p.start() for p in process] # 开启了两个进程 [p.join() for p in process] # 等待两个进程依次结束 # run__process() # 主线程不建议写在 if外部。由于这里的例子很简单,你强行这么做可能不会报错 if __name__ =='__main__': run__process() # 正确做法:主线程只能写在 if内部
Python多进程可以选择两种创建进程的方式,spawn 与 fork。分支创建:fork会直接复制一份自己给子进程运行,并把自己所有资源的handle 都让子进程继承,因而创建速度很快,但更占用内存资源。分产创建:spawn只会把必要的资源的handle 交给子进程,因此创建速度稍慢。详细解释请看 Stack OverFlow multiprocessing fork vs spawn 。(分产spawn 是我自己随便翻译的,有更好的翻译请推荐。我绝不把handle 翻译成句柄)
multiprocessing.set_start_method('spawn') # default on WinOS or MacOS
multiprocessing.set_start_method('fork') # default on Linux (UnixOS)
请注意:我说 分支fork 在初始化创建多进程的时候比 分产spawn 快,而不是说高性能计算会比较快。通常高性能计算需要让程序运行很久,因此为了节省内存以及进程安全,我建议选择 spawn。
进程池 Pool
几乎Python多进程代码都需要你明明白白地调用Process。而进程池Pool 会自动帮我们管理子进程。Python的Pool 不方便传入多个参数,我这里提供两个解决思路:
思路1:函数 func2 需要传入多个参数,现在把它改成一个参数,无论你直接让args作为一个元组tuple、词典dict、类class都可以
作者:曾伊言 链接:https://zhuanlan.zhihu.com/p/340657122 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 import time def func2(args): # multiple parameters (arguments) # x, y = args x = args[0] # write in this way, easier to locate errors y = args[1] # write in this way, easier to locate errors time.sleep(1) # pretend it is a time-consuming operation return x - y def run__pool(): # main process from multiprocessing import Pool cpu_worker_num = 3 process_args = [(1, 1), (9, 9), (4, 4), (3, 3), ] print(f'| inputs: {process_args}') start_time = time.time() with Pool(cpu_worker_num) as p: outputs = p.map(func2, process_args) print(f'| outputs: {outputs} TimeUsed: {time.time() - start_time:.1f} \n') '''Another way (I don't recommend) Using 'functions.partial'. See https://stackoverflow.com/a/25553970/9293137 from functools import partial # from functools import partial # pool.map(partial(f, a, b), iterable) ''' if __name__ =='__main__': run__pool()
管道 Pipe
顾名思义,管道Pipe 有两端,因而 main_conn, child_conn = Pipe() ,管道的两端可以放在主进程或子进程内,我在实验中没发现主管道口main_conn 和子管道口child_conn 的区别。两端可以同时放进去东西,放进去的对象都经过了深拷贝:用 conn.send()在一端放入,用 conn.recv() 另一端取出,管道的两端可以同时给多个进程。conn是 connect的缩写。
import time
def func_pipe1(conn, p_id):
print(p_id)
time.sleep(0.1)
conn.send(f'{p_id}_send1')
print(p_id, 'send1')
time.sleep(0.1)
conn.send(f'{p_id}_send2')
print(p_id, 'send2')
time.sleep(0.1)
rec = conn.recv()
print(p_id, 'recv', rec)
time.sleep(0.1)
rec = conn.recv()
print(p_id, 'recv', rec)
def func_pipe2(conn, p_id):
print(p_id)
time.sleep(0.1)
conn.send(p_id)
print(p_id, 'send')
time.sleep(0.1)
rec = conn.recv()
print(p_id, 'recv', rec)
def run__pipe():
from multiprocessing import Process, Pipe
conn1, conn2 = Pipe()
process = [Process(target=func_pipe1, args=(conn1, 'I1')),
Process(target=func_pipe2, args=(conn2, 'I2')),
Process(target=func_pipe2, args=(conn2, 'I3')), ]
[p.start() for p in process]
print('| Main', 'send')
conn1.send(None)
print('| Main', conn2.recv())
[p.join() for p in process]
if __name__ =='__main__':
run__pipe()
Pipe还有 duplex参数 和 poll() 方法 需要了解。默认情况下 duplex==True,若不开启双向管道,那么传数据的方向只能 conn1 ← conn2 。conn2.poll()==True 意味着可以马上使用 conn2.recv() 拿到传过来的数据。conn2.poll(n) 会让它等待n秒钟再进行查询。
from multiprocessing import Pipe
conn1, conn2 = Pipe(duplex=True) # 开启双向管道,管道两端都能存取数据。默认开启
#
conn1.send('A')
print(conn1.poll()) # 会print出 False,因为没有东西等待conn1去接收
print(conn2.poll()) # 会print出 True ,因为conn1 send 了个 'A' 等着conn2 去接收
print(conn2.recv(), conn2.poll(2)) # 会等待2秒钟再开始查询,然后print出 'A False'
标签:__,python,args,time,print,多线程,id,conn From: https://www.cnblogs.com/qiaoqifa/p/17648006.html