1.程序与进程
程序:程序是一个静态的概念。在一台电脑上,我们安装了很多程序,这些程序是可以运行的。比如我们编写一个xxx.py程序,它是静态的,静静的保存在电脑的硬盘中,等待执行。
进程:进程是操作系统分配资源的基本单位,一个程序运行起来后,代码加用到的系统资源称之为进程。比如我们在终端通过 python xxx.py将xxx.py程序运行起来,这时候就开启了一个进程。
在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&tpl=mn&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>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <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等实现的。