← 返回首页
Python3基础教程(四十六)
发表时间:2022-04-13 22:11:18
多进程

1.程序与进程

在Python中,可以通过multiprocessing模块开启多个进程来帮我们同时执行多任务。

2.多进程

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork函数,所以window中可以通过multiprocessing模块开启多个进程来帮我们同时执行多任务。由于Python是跨平台的,multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

# -*- coding: utf-8 -*-
# @Time : 2022/4/13 22:12
# @File : multiprocess.py
# @Software : PyCharm

from multiprocessing import Process
import os


# 子进程要执行的代码
def run_proc(name):
    print('子进程运行中,子进程 name= %s ,子进程 pid=%d, 子进程 ppid=%d' % (name, os.getpid(), os.getppid()))


if __name__ == '__main__':
    print('父进程pid: %d.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将要执行')
    p.start()
    p.join()
    print('子进程已结束')

运行结果:

父进程pid: 3488.
子进程将要执行
子进程运行中,子进程 name= test ,子进程 pid=1336, 子进程 ppid=3488
子进程已结束

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

# -*- coding: utf-8 -*-
# @Time : 2022/4/13 22:31
# @File : pooldemo.py
# @Software : PyCharm

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %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.')

运行结果:

Parent process 13864.
Waiting for all subprocesses done...
Run task 0 (7720)...
Run task 1 (11484)...
Run task 2 (9252)...
Run task 3 (13724)...
Task 1 runs 0.29 seconds.
Run task 4 (11484)...
Task 3 runs 0.86 seconds.
Task 4 runs 1.78 seconds.
Task 2 runs 2.32 seconds.
Task 0 runs 2.60 seconds.
All subprocesses done.

4.子进程 很多时候,子进程并不是自身,而是一个外部进程。我们创建了子进程后,还需要控制子进程的输入和输出。subprocess模块可以让我们非常方便地启动一个子进程,然后控制其输入和输出。

实例:

# -*- coding: utf-8 -*-
# @Time : 2022/4/14 17:46
# @File : subprocessdemo.py
# @Software : PyCharm

import subprocess

print('$ curl www.baidu.com')
# 给百度网发送请求
r = subprocess.call(['curl', 'www.baidu.com'])
print('Exit code:', r)

运行结果:

$ curl www.baidu.com
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2381  100  2381    0     0  14346      0 --:--:-- --:--:-- --:--:-- 14518
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
Exit code: 0

5.进程间通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据。

实例:

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__':
    # 父进程创建Queue,并传给各个子进程:
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw,写入:
    pw.start()
    # 启动子进程pr,读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环,无法等待其结束,只能强行终止:
    pr.terminate()

运行结果:

Process to write: 7376
Put A to queue...
Process to read: 12748
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.

小结: - 在Unix/Linux下,可以使用fork()调用实现多进程。windows下使用multiprocessing。 - 要实现跨平台的多进程,可以使用multiprocessing模块。 - os.getpid()可以获取当前进程编号,os.getppid()获取父进程编号。 - 创建进程池可以使用Pool。 - subprocess模块可以启动子进程 - 进程间通信是通过Queue、Pipes等实现的。