多进程 multiprocessing模块
multiprocessing模块提供了一个Process类来代表一个进程对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#!/usr/bin/env python3 # coding:utf-8 ''' Created on: 2016年3月5日 @author: 张晓宇 Email: 61411916@qq.com Version: 1.0 Description: 多进程演示程序 Help: ''' from multiprocessing import Process import os def run_proc(name): # 子进程要执行的函数 print ( 'Run child process %s (%s)...' % (name, os.getpid())) # os.getpid()表示获得当前进程的pid if __name__ = = '__main__' : print ( 'Parent process %s.' % os.getpid()) # 打印父进程的pid p = Process(target = run_proc, args = ( 'test' ,)) # 创建进程对象,参数结构和多线程一样 print ( 'Child process will start.' ) p.start() # 启动子进程 p.join() # 阻塞等待子进程执行完毕 print ( 'Child process end.' ) |
进程间通信
Queue
不同进程间内存是不共享,所以多进程不能像多线程一样通过全局变量(当然全局变量也是不提倡的),所以只能通过队列,多进程模块也自带一个队列Queue,使用方法和threading里的queue差不多
Pipe
管道,可以理解为两个进程之间的一个桥梁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#!/usr/bin/env python3 # coding:utf-8 ''' Created on: 2016年3月5日 @author: 张晓宇 Email: 61411916@qq.com Version: 1.0 Description: 管道演示程序 Help: ''' from multiprocessing import Process, Pipe def f(conn): conn.send([ 42 , None , 'hello' ]) # 网管道里传递数据 conn.close() if __name__ = = '__main__' : parent_conn, child_conn = Pipe() # 一个是父进程的管道对象,一个是子进程的对象,自己成往里面send,父进程对象recv,有点像socket p = Process(target = f, args = (child_conn,)) # 把管道对象作为参数传递给子进程 p.start() print (parent_conn.recv()) # 接收管道里的数据并打印出来 p.join() |
执行结果
1
|
[ 42 , None , 'hello' ] |
有人会说既然可以往子进程要执行的而函数传递参数,直接通过这个参数取子进程传递过来的数据就好了,比如可以用列表等可变数据类型(字符串和数值型等不可变类型的数据,想都不要想,统一进程都做不到)为啥还用管道或队列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#!/usr/bin/env python3 # coding:utf-8 ''' Created on: 2016年3月5日 @author: 张晓宇 Email: 61411916@qq.com Version: 1.0 Description: 管道演示程序 Help: ''' from multiprocessing import Process, Pipe def f(conn, strinfo): conn.send([ 42 , None , 'hello' ]) # 网管道里传递数据 conn.close() # 关闭管道 strinfo.append( 'child' ) if __name__ = = '__main__' : parent_conn, child_conn = Pipe() # 一个是父进程的管道对象,一个是子进程的对象,自己成往里面send,父进程对象recv,有点像socket strinfo = [ 'parent' ] p = Process(target = f, args = (child_conn, strinfo)) # 把管道对象作为参数传递给子进程 p.start() print (parent_conn.recv()) # 接收管道里的数据并打印出来 print (strinfo) p.join() |
执行结果
1
2
|
[ 42 , None , 'hello' ] ['parent'] |
从执行结果中可以看出来,strinfo的值并没有变化,那是因为,进程启动的时候重新划分了内存空间,等于将strinfo在子进程中copy了一份,已经和父进程中的strinfo没有半毛钱关系了所以要有管道队列等
进程池
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程, 如果进程池序列没有可提供的进程,那么就会等待,知道有可用进程为止
Pool模块有两种常用的启动进程的方法
apply和apply_assync,从字面上理解是apply_assync是异步的,其实就是apply_assync支持把一个函数作为参数传递进去,当进程函数执行完的时候可以通过return一个值,这个值,会自动作为参数传递个传递进来的函数,并执行该函数,我们称之为回调(callback)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
#!/usr/bin/env python # coding:utf-8 ''' Created on: 2016年3月5日 @author: 张晓宇 Email: 61411916@qq.com Version: 1.0 Description: 进程池演示程序 Help: ''' from multiprocessing import Pool, freeze_support import time def Foo(i): ''' 子进程执行的函数 :param i: :return: ''' time.sleep( 2 ) return i + 100 def Bar(arg): ''' 子进程回调函数 :param arg: :return: ''' print ( '-->exec done:' ,arg) if __name__ = = '__main__' : # 这个在windows环境中绝对不能省略否则会报错 freeze_support() pool = Pool( 5 ) # 创建进程池对象 for i in range ( 10 ): pool.apply_async(func = Foo, args = (i,), callback = Bar) # pool.apply(func=Foo, args=(i,)) print ( 'end' ) pool.close() pool.join() #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。 |
执行结果
1
2
3
4
5
6
7
8
9
10
11
|
end - - > exec done: 100 - - > exec done: 101 - - > exec done: 102 - - > exec done: 103 - - > exec done: 104 - - > exec done: 105 - - > exec done: 106 - - > exec done: 107 - - > exec done: 108 - - > exec done: 109 |