
其实multiprocessing模块与threading模块的接口都非常相似,但是有一些地方有一些细微差别。所以本文是基于前面的threading模块的一些知识对multiprocessing模块进行讲解的。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
他们的主要区别有以下几点@H_301_2@@H_301_2@
1.创建子进程的方式针对不同平台有着差异化@H_301_2@@H_301_2@
2.关于守护线程的设置接口是
setDaemon(True),而关于守护进程的接口是deamon = True@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@3.
multiprocessing模块下的获取进程名与设置进程名没有threading模块下的getname()和setname(),而是直接采取属性name进行 *** 作@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@4.多进程中数据共享不能使用普通的
queue模块下提供的队列进行数据共享,而应使用multiprocessing中提供的Queue@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@5.
multiprocessing模块下中提供的Queue先进先出队列没有task_done()与join(),他们都在JoinableQueue中,并且该模块下没有提供lifoQueue后进先出队列与PriorityQueue优先级队列@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
官方中文文档@H_301_2@@H_301_2@
threading模块基本使用@H_301_2@@H_301_2@@H_301_2@
多进程与多线程工作的区别@H_301_2@多线程工作方式@H_301_2@多线程的工作方式实际上在第一篇的时候,我们已经说过了。因为线程必须存在于进程之中,是最小的执行单元,所以你可以将它如此理解:@H_301_2@@H_301_2@
@H_301_2@
其实就是不断的往进程这个小房间加人,那么它的优点如下:@H_301_2@@H_301_2@
开一条新的线程比开一条新的进程开销要小很多@H_301_2@@H_301_2@
并且对于线程的切换来说代价也要小很多@H_301_2@@H_301_2@
多条线程共有该进程下的所有资源,数据共享比较容易实现@H_301_2@@H_301_2@
而cpython由于GIL锁的设定,所以它的多线程是残缺不全的,因此在很多时候我们依然要用到多进程,虽然这种情况比较少。@H_301_2@@H_301_2@
多进程工作方式@H_301_2@@H_301_2@
其实就是不断的造出一模一样的小房间,那么它的优点如下:@H_301_2@@H_301_2@
虽然说,新开一条进程比新开一条线程的代价大很多,但是由于cpython中GIL锁的设定想在多线程的情况下实现并行是不可能的,只有多进程才能够实现并行。@H_301_2@@H_301_2@
可以说是唯一优点了,但是我们依然要学习一下multiprocessing模块,它的学习代价并不是很大,所以接下来正式进入multiprocessing模块的学习。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
对于进程启动方式来说,其实multiprocessing模块中对于不同平台下有不同的启动方式。如下:@H_301_2@@H_301_2@@H_301_2@@H_301_2@
spawn:这玩意儿相当于创建了一个新的解释器进程,对比其他两种方法,这种方法速度上比较慢,但是它是windows平台下默认的启动方式(Unix系统下可用)。并且在windows平台下,我们应该在if __name__ == '__main__'下进行新进程的启动。但是我依然认为不管在哪个平台下不论线程还是进程都应该在if __name__ == '__main__'这条语句下启动。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
fork:这种启动方式是通过os.fork()来产生一个新的解释器分叉,是Unix系统的默认启动方式。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
forkserver:这个我也看不太明白,直接把官方文档搬过来。如果有懂的大神可以解释一下。@H_301_2@@H_301_2@@H_301_2@程序启动并选择
forkserver启动方法时,将启动服务器进程。从那时起,每当需要一个新进程时,父进程就会连接到服务器并请求它分叉一个新进程。分叉服务器进程是单线程的,因此使用os.fork()是安全的。没有不必要的资源被继承。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@可在Unix平台上使用,支持通过Unix管道传递文件描述符。@H_301_2@
import@H_301_2@ multiprocessing as mp@H_301_2@def@H_301_2@ foo(q): q.put(@H_301_2@'@H_301_2@hello@H_301_2@'@H_301_2@)@H_301_2@if@H_301_2@ __name__@H_301_2@ == __main__@H_301_2@'@H_301_2@: #@H_301_2@ <--- 强烈注意!在windows平台下开多进程一定要在该条语句之下,否则会抛出异常!!@H_301_2@ mp.set_start_method(spawn@H_301_2@'@H_301_2@) 选择启动方式@H_301_2@ q = mp.Queue() 实例化出用于进程间数据共享的管道@H_301_2@ p = mp.Process(target=foo,args=(q,)) p.start() @H_301_2@ 启动进程任务,等待cpu调度执行@H_301_2@ print@H_301_2@(q.get()) 从管道中拿出数据@H_301_2@ p.join() 阻塞至子进程运行完毕@H_301_2@@H_857_419@实例化Process类创建子进程@H_301_2@ 其实感觉上面的方法都已经将本章要写的内容举例了一个七七八八,但是我们接着往下看。与threading模块中创建多线程的方式一样,multiprocessing模块创建多进程的方式也有两种,所以我们将之前的示例拿过来直接改一改就好。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
multiprocessing@H_301_2@ time@H_301_2@print@H_301_2@("@H_301_2@主进程任务开始处理@H_301_2@"@H_301_2@)@H_301_2@ task(th_name): @H_301_2@子进程任务开始处理,参数:{0}@H_301_2@.format(th_name)) time.sleep(@H_301_2@3) @H_301_2@子进程任务处理完毕@H_301_2@'@H_301_2@: <--- windows平台下必须在该条语句下执行@H_301_2@ ==== 实例化出Process类并添加子进程任务以及参数 ====@H_301_2@ p1 @H_301_2@= multiprocessing.Process(target=task,args=(进程[1]@H_301_2@"@H_301_2@,)) <-- 参数必须添加逗号。因为是args所以会打散,如果不加逗号则不能进行打散会抛出异常@H_301_2@ p1.start() 等待cpu调度..请注意这里不是立即执行@H_301_2@ 主进程任务处理完毕@H_301_2@ ==== 执行结果 ====@H_301_2@"""@H_301_2@主进程任务开始处理主进程任务处理完毕主进程任务开始处理子进程任务开始处理,参数:进程[1]子进程任务处理完毕@H_301_2@"""@H_301_2@@H_857_419@ 这里我们看执行结果,主进程任务开始处理打印了两次,而主进程任务处理完毕打印了一次,这是为什么呢?由于我们是在windows平台下,所以它默认的进程启动方式为spawn,即创建了一个新的解释器进程并开始执行,所以上面的主进程任务开始处理就打印了两次,一次是主进程,一次是新创建的子进程。而下面由于if __name__ == '__main__':这条语句,子进程并不会执行该语句下面的代码块,所以主进程任务处理完毕就只打印了一次。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
@H_301_2@
自定义类继承Process并覆写run方法@H_301_2@class@H_301_2@ Processing(multiprocessing.Process): @H_301_2@自定义类@H_301_2@"""@H_301_2@ def@H_301_2@ __init__@H_301_2@(self,th_name): self.th_name @H_301_2@= th_name super(Processing,self).@H_301_2@() @H_301_2@ run(self): @H_301_2@.format(self.th_name)) time.sleep(@H_301_2@3) @H_301_2@: p1 @H_301_2@= Processing() p1.start() @H_301_2@"""@H_301_2@@H_857_419@multiprocessing方法大全@H_301_2@ multiprocessing模块中的方法参考了thrading模块中的方法。但是我们一般用下面两个方法就够了,他们都可以拿到具体的进程对象。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
| multiprocessing模块方法大全@H_301_2@@H_301_2@ @H_301_2@ | |
|---|---|
| 方法/属性名称 @H_301_2@@H_301_2@ | 功能描述@H_301_2@@H_301_2@ |
| multiprocessing.active_children()@H_301_2@@H_301_2@ | 查看当前进程存活了的所有子进程对象,以列表形式返回。@H_301_2@@H_301_2@ |
| multiprocessing.current_process()@H_301_2@@H_301_2@ | 获取当前进程对象。@H_301_2@@H_301_2@ |
| 好伙伴os模块@H_301_2@@H_301_2@ | |
|---|---|
| 方法/属性名称@H_301_2@ | 功能描述@H_301_2@ |
| os.getpID()@H_301_2@ | 返回进程ID。@H_301_2@ |
| os.getppID()@H_301_2@ | 返回当前进程的父进程ID。@H_301_2@ |
| 进程对象方法大全(即Process类的实例对象)@H_301_2@@H_301_2@ @H_301_2@ | |
|---|---|
| 方法/属性名称@H_301_2@@H_301_2@ | 功能描述@H_301_2@@H_301_2@ |
| start()@H_301_2@@H_301_2@ | 启动进程,该方法不会立即执行,而是告诉cpu自己准备好了,可以随时调度,而非立即启动。@H_301_2@@H_301_2@ |
| run()@H_301_2@@H_301_2@ | 一般是自定义类继承Process类并覆写的方法,即线程的详细任务逻辑。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| join(timeout=None)@H_301_2@@H_301_2@@H_301_2@@H_301_2@ | 主进程默认会等待子进程运行结束后再继续执行,timeout为等待的秒数,如不设置该参数则一直等待。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| name@H_301_2@@H_301_2@ | 可以通过 = 给该进程设置一个通俗的名字。如直接使用该属性则返回该进程的默认名字。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| is_alive()@H_301_2@@H_301_2@ | 查看进程是否存活,返回布尔值。@H_301_2@@H_301_2@ |
| daemon@H_301_2@@H_301_2@ | 可以通过 = 给该进程设置一个守护进程。如直接使用该属性则是查看进程是否为一个守护进程,返回布尔值。默认为False。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| pID | 返回进程ID。在生成该进程之前,这将是 None 。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| exitcode@H_301_2@@H_301_2@ | 子进程的退出代码。如果进程尚未终止,这将是 None 。负值 -N 表示子进程被信号 N 终止。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| authkey@H_301_2@@H_301_2@ | 进程的身份验证密钥(字节字符串)。@H_301_2@@H_301_2@ |
| sentinel@H_301_2@@H_301_2@ | 系统对象的数字句柄,当进程结束时将变为 "ready" 。@H_301_2@@H_301_2@ |
| terminate()@H_301_2@@H_301_2@ | 终止进程。@H_301_2@@H_301_2@ |
| kill()@H_301_2@@H_301_2@ | 同上@H_301_2@@H_301_2@ |
| close()@H_301_2@@H_301_2@ | 关闭 Process 对象,释放与之关联的所有资源。如果底层进程仍在运行,则会引发 ValueError 。一旦 close() 成功返回, Process 对象的大多数其他方法和属性将引发 ValueError 。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
注意 start() 、 join() 、 is_alive() 、 terminate() 和 exitcode 方法只能由创建进程对象的进程调用。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ | |
) @H_301_2@: p1 @H_301_2@= multiprocessing.Process(target=task,1)">,)) p1.daemon @H_301_2@= True <-- 设置进程对象p1为守护进程,注意这一步一定要放在start之前。@H_301_2@ p1.start() 等待cpu调度..请注意这里不是立即执行@H_301_2@ time.sleep(@H_301_2@2) @H_301_2@ ==== 执行结果 ==== # print("子进程任务处理完毕") 可以看到该句没有执行@H_301_2@ 主进程任务开始处理主进程任务开始处理子进程任务开始处理,参数:进程[1]主进程任务处理完毕@H_301_2@"""@H_301_2@@H_857_419@ 设置与获取进程名@H_301_2@.format(th_name)) obj @H_301_2@= multiprocessing.current_process() 获取当前进程对象@H_301_2@ 获取当前的进程名:{0}@H_301_2@.format(obj.name)) @H_301_2@开始设置进程名@H_301_2@) obj.name @H_301_2@= yyy@H_301_2@"@H_301_2@ 获取修改后的进程名:{0}@H_301_2@.format(obj.name)) time.sleep(@H_301_2@3: @H_301_2@ ==== 第一步:实例化出Process类并添加子进程任务以及参数 ====@H_301_2@ t1 @H_301_2@= multiprocessing.Process(target=task,),name=xxx@H_301_2@) t1.start() @H_301_2@主进程名:@H_301_2@ 直接使用属性 name@H_301_2@ 主进程任务开始处理主进程名: MainProcess主进程任务处理完毕主进程任务开始处理子进程任务开始处理,参数:进程[1]获取当前的进程名:xxx开始设置进程名获取修改后的进程名:yyy子进程任务处理完毕@H_301_2@"""@H_301_2@@H_857_419@ 锁相关演示@H_301_2@ 锁的使用和threading模块中锁的使用相同,所以我们举例一个Lock锁即可。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
multiprocessinglock @H_301_2@= multiprocessing.Lock() 实例化同步锁对象 # 注意!!! 在windows平台下,我们应该将锁的实例化放在上面,这样子进程才能拿到锁对象。否则就会抛出异常!!!或者也可以将锁对象传入当做形参进行传入,二者选其一@H_301_2@num @H_301_2@= 0@H_301_2@ add(): lock.acquire() @H_301_2@ 上锁@H_301_2@ global@H_301_2@ num @H_301_2@for@H_301_2@ i in@H_301_2@ range(10000000): 一千万次@H_301_2@ num += 1 lock.release() @H_301_2@ 解锁@H_301_2@ sub(): lock.acquire() @H_301_2@ 一千万次@H_301_2@ num -= 1: t1 @H_301_2@= multiprocessing.Process(target=add,) t2 @H_301_2@= multiprocessing.Process(target=sub,) t1.start() t2.start() t1.join() t2.join() @H_301_2@最终结果:@H_301_2@ ==== 执行结果 ==== 三次采集@H_301_2@最终结果: 0最终结果: 0最终结果: 0@H_301_2@"""@H_301_2@@H_857_419@from@H_301_2@ multiprocessing Process,Lock@H_301_2@ f(l,i): l.acquire() @H_301_2@try@H_301_2@: @H_301_2@hello world@H_301_2@finally@H_301_2@: l.release()@H_301_2@: lock @H_301_2@= Lock() 将锁实例化后传入@H_301_2@for@H_301_2@ num in@H_301_2@ range(10): Process(target@H_301_2@=f,args=(lock,num)).start()@H_857_419@将锁当做参数传入@H_301_2@@H_857_419@三种进程数据共享的方式@H_301_2@multiprocessing.Queue@H_301_2@ 这里一定要使用multiprocessing中的Queue,如果你想用队列中的task_done()与join()方法,你应该导入JoinableQueue这个队列。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
| multiprocessing.Queue方法大全@H_301_2@@H_301_2@ @H_301_2@ | |
|---|---|
| 方法名称@H_301_2@@H_301_2@ | 功能描述@H_301_2@@H_301_2@ |
| Queue.qsize()@H_301_2@@H_301_2@ | 返回当前队列的大小@H_301_2@@H_301_2@ |
| Queue.empty()@H_301_2@@H_301_2@ | 判断当前队列是否为空@H_301_2@@H_301_2@ |
| Queue.full()@H_301_2@@H_301_2@ | 判断当前队列是否已满@H_301_2@@H_301_2@ |
| Queue.put(item,block=True,timeout=None)@H_301_2@@H_301_2@ | 将item放入队列中,block参数为如果要 *** 作的队列目前已满是否阻塞,timeout为超时时间。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.put_Nowait(item)@H_301_2@@H_301_2@ | 相当于 put(item,False),如果 *** 作的队列已满则不进行阻塞,而是抛出Full异常。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.get(block=True,timeout=None)@H_301_2@@H_301_2@ | 将项目从队列中取出,block参数为如果要 *** 作的队列目前为空是否阻塞,timeout为超时时间。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.get_Nowait()@H_301_2@@H_301_2@ | 相当于 get(False),如果要 *** 作的队列为空则不进行阻塞,而是抛出Empty异常。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.close()@H_301_2@@H_301_2@ | 指示当前进程将不会再往队列中放入对象。一旦所有缓冲区中的数据被写入管道之后,后台的线程会退出。这个方法在队列被gc回收时会自动调用。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.join_thread()@H_301_2@@H_301_2@ | 等待后台线程。这个方法仅在调用了 close() 方法之后可用。这会阻塞当前进程,直到后台线程退出,确保所有缓冲区中的数据都被写入管道中。@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
| Queue.cancel_join_thread()@H_301_2@@H_301_2@ | 防止 join_thread() 方法阻塞当前进程。具体而言,这防止进程退出时自动等待后台线程退出。详见 join_thread()。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@ |
进程队列multiprocessing.Queue不同于线程队列queue.Queue,进程队列的消耗和底层实现比线程队列的要复杂许多。还是因为各进程之间不能共享任何数据,所以只能通过映射的方式来传递数据。进程队列multiprocessing.Queue作为数据安全类型的数据结构,放在多进程中做通信使用是非常合适的,但是同时它的消耗也是非常大的,能不使用则尽量不要使用。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
time@H_301_2@ Queue,JoinableQueue@H_301_2@ task_1(q): @H_301_2@正在装东西..@H_301_2@) time.sleep(@H_301_2@3) q.put(@H_301_2@玫瑰花@H_301_2@"@H_301_2@) 正在装东西@H_301_2@ q.task_done() 通知对方可以取了@H_301_2@ task_2(q): q.join() @H_301_2@ 阻塞等待通知,接到通知说明队列里里有东西了。@H_301_2@ 取到了@H_301_2@ 取东西@H_301_2@: q @H_301_2@= JoinableQueue(maxsize=5) 实例化队列@H_301_2@ t1 @H_301_2@= multiprocessing.Process(target=task_1,args=(q,1)">小明@H_301_2@ 将队列传进子进程任务中@H_301_2@ t2 = multiprocessing.Process(target=task_2,1)">小花@H_301_2@) t1.start() t2.start()@H_301_2@正在装东西..取到了 玫瑰花@H_301_2@"""@H_301_2@@H_857_419@进程队列Queue实现进程间的数据共享@H_301_2@@H_857_419@ 为什么线程队列queue.Queue不能做到进程间数据共享呢,这是因为进程队列multiprocessing.Queue会采取一种映射的方式来同步数据,所以说进程队列的资源消耗比线程队列要庞大很多。线程中所有信息共享,所以线程队列根本不需要映射关系。进程队列只是告诉你可以这样使用它达到进程间的数据共享,但是并不推荐你滥用它。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
@H_301_2@
multiprocessing.Pipe@H_301_2@ 除开使用进程队列来实现进程间的通信,multiprocessing还提供了Pipe管道来进行通信。他的资源消耗较少并且使用便捷,但是唯一的缺点便是只支持点对点。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
Pipe有点类似socket通信。但是比socket通信更加简单,它不需要去做字符串处理字节,先来看一个实例:@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
Pipe@H_301_2@ task_1(conn1): conn1.send(@H_301_2@hello,我是task1@H_301_2@print@H_301_2@(conn1.recv())@H_301_2@ task_2(conn2): @H_301_2@(conn2.recv()) conn2.send(@H_301_2@我收到了,我是task2@H_301_2@: conn1,conn2 @H_301_2@= Pipe() 创建两个电话@H_301_2@ p1 = multiprocessing.Process(target=task_1,args=(conn1,1)"> 一人一部电话@H_301_2@ p2 = multiprocessing.Process(target=task_2,1)">(conn2,)) p1.start() p2.start() p1.join() p2.join()@H_301_2@hello,我是task1我收到了,我是task2@H_301_2@"""@H_301_2@@H_857_419@Pipe实现进程间的数据共享@H_301_2@@H_857_419@@H_301_2@
multiprocessing.Manager@H_301_2@ 除了进程队列multiprocessing.Queue,管道Pipe,multiprocessing还提供了Manager作为共享变量来提供使用,但是这种方式是不应该被直接使用的因为它本身相较于进程队列Queue是数据不安全的。当多个进程同时修改一个共享变量势必导致结果出现问题,所以要想使用共享变量还得使用multiprocessin提供的进程锁才行。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
Manager类是数据不安全的;@H_301_2@@H_301_2@@H_301_2@
Mangaer类支持的类型非常多,如:value,Array,List,Dict,Queue(进程池通信专用),Lock等。@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
Mangaer实现了上下文管理器,可使用with语句创建多个对象。具体使用方法我们来看一下:@H_301_2@@H_301_2@@H_301_2@@H_301_2@@H_301_2@
Manager@H_301_2@ task_1(dic): dic[@H_301_2@task_1@H_301_2@"@H_301_2@] = 大帅哥@H_301_2@"@H_301_2@ task_2(dic): dic[@H_301_2@task_2@H_301_2@大美女@H_301_2@print@H_301_2@(dic.get())@H_301_2@: with Manager() as m: @H_301_2@ !!!!! 注意 !!!!!!! 如果对 Manager()中的数据类型进行频繁的 *** 作,而进程又特别多的时候,请使用 Rlock 锁进行处理,这有可能引发线程不安全!!!@H_301_2@ dic = m.dict() 实例化出了一个字典,除此之外还有很多其他的数据类型@H_301_2@ p1 @H_301_2@= multiprocessing.Process(target=task_1,args=(dic,1)"> 将字典传进来@H_301_2@ p2 = multiprocessing.Process(target=task_2,1)">(dic,)) p1.start() @H_301_2@ 启动一定要放在with之后@H_301_2@ p2.start() p1.join() p2.join()@H_301_2@大帅哥@H_301_2@"""@H_301_2@@H_857_419@Manager实现进程间的数据共享@H_301_2@@H_857_419@ task_1(dic): @H_301_2@in@H_301_2@ range(1000): dic[@H_301_2@count@H_301_2@"@H_301_2@] += 1 task_2(dic): @H_301_2@"@H_301_2@] -= 1 !!!!! 注意 !!!!!!! 如果对 Manager()中的数据类型进行频繁的 *** 作,而进程又特别多的时候,请使用 Rlock 锁进行处理,这有可能引发线程不安全!!!@H_301_2@ dic = m.dict({"@H_301_2@:0}) 传字典@H_301_2@ p2 = multiprocessing.Process(target=task_2,)) p1.start() p2.start() p1.join() p2.join() @H_301_2@(dic)@H_301_2@{'count': -23}@H_301_2@"""@H_301_2@@H_857_419@进程安全问题@H_301_2@@H_857_419@ Manager@H_301_2@ RLock@H_301_2@ task_1(dic,lock): with lock: @H_301_2@): dic[@H_301_2@ task_2(dic,1)">: lock @H_301_2@= RLock() 实例化锁@H_301_2@ with Manager() as m: @H_301_2@ 传字典,传锁@H_301_2@ p2 = multiprocessing.Process(target=task_2,1)">{'count': 0}@H_301_2@"""@H_301_2@@H_857_419@实用Rlock锁解决进程安全问题@H_301_2@@H_857_419@ 总结 以上是内存溢出为你收集整理的multiprocessing多进程模块全部内容,希望文章能够帮你解决multiprocessing多进程模块所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)