本文中主要介绍python语言中的process模块及简单实用

进程和线程

进程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from multiprocessing import Process, Pool
import os, time, random, subprocess

print(os.getpid())


pid = os.fork()


if pid == 0:

	print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getpid()))

else:

	print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

有了fork()调用,一个进程在接到新任务的时候就可以复制出一个子进程来处理新任务,常见的apache服务器就是由父进程监听端口,每当有新的http请求的时候,就fork出子进程来处理新的请求

由于Windows没有fork调用,所以以上代码在Windows上无法调用。Mac系统是给予BSD(Unix的一种),所以完全ok

由于python是跨平台的,自然也提供了跨平台的多进程支持:multiprocessing模块

多进程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def run_process(name):

	print('Run child process %s (%s).' % (name, os.getpid()))



if __name__ == '__main__':

    print('Parent process %s.' % os.getpid())

    p = Process(target=run_process, args=('test',))

    print('Child process will start.')

    p.start()

    p.join()

    print('Child process end')

join()方法可以等待子进程结束以后在继续往下执行,通常用于进程间的通信

如果要启动大量的子进程,可以使用进程池的方式批量创建子进程

 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
def long_time_task(name):

    print('Run task as %s (%s)...' % (name, os.getpid()))

    start = time.time()

    time.sleep(random.random() * 3)

    end = time.time()

    print('Task %s runs %0.2f seconds.' % (name, (end - start)))



if __name__ == '__main__':

    print('Parent process %s.' % os.getpid())

    p = Pool(4)


    for i in range(5):

        p.apply_async(long_time_task, args=(i,))


    print("waiting for all subProcesses done...")

    p.close()

    p.join()

    print('All subProcesses done.')
  • 对Pool对象调用join()方法会等待所有子进程执行完毕,调用join之前必须先调用close,调用close之后就不能继续添加新的process了
  • task 0,1,2,3是立刻执行的,但是task4要等待前面某个task完成之后才执行,这是因为pool的默认大小在我的电脑上是4,所以最多同时执行4 这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

子进程

很多时候 子进程并不是本身,而是一个外部进程。我们创建了子进程后 还需要控制输入输出

subprocess模块可以让我们非常方便的启动一个子进程,然后控制其输入和输出

1
2
3
4
5
print('$ nslookup www.python.org')

r = subprocess.call(['nslookup', 'www.python.org'])

print('Exit code:', r)

如果子进程还需要输入 可以通过communicate()方法输入

1
2
3
4
5
6
7
8
9
print('$ nslookup')

p = subprocess.Popen(['nslookup'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

output, err = p.communicate(b'set q=mx\npython.org\nexit\n')

print(output.decode('utf-8'))

print('Exit code:', p.returncode)

进程间通信

  • process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。python的multiprocess模块包装了底层的机制,提供了Queue,Pipes等多种方式来交换数据
  • 我们以Queue为例,在父进程中创建两个子进程,实现队列中的读数据和写数据
 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
46
47
48
49
50
from multiprocessing import Process, Queue

import os, time, random


def write(q):

    print('Process to write:%s.' % os.getpid())

    for value in ['A', 'B', 'C']:

        print('Put %s to queue...' % value)

        q.put(value)

        time.sleep(random.random())





def read(q):

    print('Process to read %s' % os.getpid())

    while True:

        value = q.get(True)

        print('Get %s from queue.' % value)





if __name__ == '__main__':

    q = Queue()

    pw = Process(target=write, args=(q,))

    pr = Process(target=read, args=(q,))

    pw.start()

    pr.start()

    pw.join()

    pr.terminate()

小结

  • 在Unix/Linux下,可以使用fork()调用实现多进程。
  • 要实现跨平台的多进程,可以使用multiprocessing模块。
  • 进程间通信是通过Queue、Pipes等实现的。