[Python]基于进程的并行

1 subprocess - 生成多余进程

subprocess目的是开始与其他进程交互,即用来生成子进程,并可以通过管道连接它们的输入/输出/错误,以及获得它们的返回值。

subprocess.run()函数是在python 3.5以后被添加的,具体参数列表参见python文档,下面只解释几个常用的参数描述。

subprocess.run(args, capture_out=False, shell=False, timeout=None, check=False)

  • 作用: 调用子进程,并返回进程状态码,如果一个进程被执行并结束,则返回0.

params:

  • args:指令 ,如果shell=False,则使用python解析器解析(等同于subprocess.call(args)),args为命令参数列表,如果shell=True,则使用shell自己解析,则args为命令字符串
  • capture_out:如果capture_out设为true,stdout和stderr将会被捕获;
  • timeout:见文档;
  • check:如果check设为true,并且进程以非零状态码退出,一个CalledProcessError异常将会被抛出。

2 Multiprocessing - 基于进程的并行

在Unix/Linux系统中提供了一个fork()函数,它比较特殊,普通函数调用一次,返回一次,而fork()函数调用一次,返回两次,因为操作系统会自动地把当前系统复制一份(也就是子进程),然后在父进程和子进程中分别返回. 其中,父进程返回子进程号,而子进程永远返回0.

I. 上下文和启动方法

根据不同的平台,multiprocessing支持三种启动进程的方法. 这些启动方法有:

  1. spawn

    父进程启动一个新的Python解释器进程,子进程只会继承那些运行进程对象的run()方法所需的资源,相对于使用fork或forserver,使用这个方法启动进程相当慢。可在Unix和Windows上使用,是在windows上的默认设置。

  2. fork

    父进程使用os.fork()来产生Python解释器分叉,只存在于Unix(Unix中的默认值)。

  3. forkserver

    程序启动并选择* forkserver * 启动方法时,将启动服务器进程。可在Unix平台上使用。

启动方法:

可以使用multiprocessing.set_start_methond('spawn')multiprocessing.get_context('spawn')启动。

  1. 在程序中set_start_method()不应该被多次调用;
  2. 或者,可以使用get_context()来获取上下文对象(获取一个按指定启动方法启动的multiprocessing对象)。上下文对象与多处理模块具有相同的API,并允许在统一程序中使用多个启动方法

II. 进程间通信

进程间可以使用Queue或者Pipe来进行通信。

1. Queue用来在多个进程间进行通信

Queue用来在多个进程间进行通信,Queue主要使用两个方法,get()和put().

put(obj [, block=True [, timeout=None]])

  • 将obj放入到队列中;
  • 如果可选参数block=True,(1)并且timeout是None,将会阻塞当前进程,直到有空的缓冲槽;(2)如果timeout是正数,将会在最多阻塞了timeout秒后,如果还是没有可用的缓冲槽时将抛出queue.Full异常。
  • 如果可选参数block=False(此时timeout参数会被忽略掉),仅当有可用缓冲槽时才放入队列中,否则抛出queue.Full异常。

get([block [, timeout]]):

  • 从队列中取出并返回对象;
  • block和timeout的作用与put类似,详见python文档。

下面是一个代码实例:

import multiprocessing
import os
from multiprocessing import Process
import time
import random
import multiprocessing

def write_process(q, tokens):
    # 需要执行的子进程
    print("Write Process`s writing, process id is {:d}.".format(os.getpid()))
    for token in tokens:
        q.put(token)
        print('put {:s} to the queue.'.format(token))
        time.sleep(random.random())

def read_process(q):
    print("Read Process`s reading, process id is {:d}.".format(os.getpid()))
    # while not q.empty():
    #     token = q.get()
    #     print("get {:s} from the queue.".format(token))
    while True:
        token = q.get()
        print("get {:s} from the queue.".format(token))

if __name__ == "__main__":
    q = multiprocessing.Queue()
    # 实例化子进程
    pw1 = Process(target=write_process, args=(q, ['t1', 't2', 't3']))
    pw2 = Process(target=write_process, args=(q, ['t4', 't5']))
    pr = Process(target=read_process, args=(q, ))
    # 开始执行子进程
    pw1.start()
    pw2.start()
    pr.start()
    pw1.join()
    pw2.join()
    # pr.join() 如果read_process使用 not q.empty()作为判断条件,由于多进程的异步性,会有元素无法从队列中取出
    # read_process里是一个死循环,只能强制终止
    pr.terminate()

执行结果

Write Process`s writing, process id is 20408.Read Process`s reading, process id is 23268.

Write Process`s writing, process id is 19232.
put t1 to the queue.put t4 to the queue.

get t1 from the queue.
get t4 from the queue.
put t5 to the queue.get t5 from the queue.

put t2 to the queue.
get t2 from the queue.
put t3 to the queue.
get t3 from the queue.

扩展

在最近的项目中碰到了multiprocessing.SimpleQueue,记录一下.

class multiprocessing.SimpleQueue

- 这是一个简化的Queue类的实现,很像带锁的Pipe.

  1. empty()

    - 如果队列为空则返回True,否则返回False.

  2. get()

    - 从队列中移除并返回一个对象.

  3. put(item)

    - 将item放入队列.

2. Pipe常用来在两个进程间通信

Pipe常用来在两个进程间通信,两个进程分别位于管道的两端.

multiprocessing.Pipe([duplex=True])

- 返回一对Connection对象,(conn1, conn2)分别表示管道的两端;

- 如果duplex被置为True,那么该管道是双向的,如果为False,那么该管道是单向的,即conn1只能用于接收消息,conn2只能用于发送消息.

管道是将一系列标准输入输出链接起来的进程,其中每一个进程的输出被直接作为下一个进程的输入.

代码实例:

from multiprocessing import Pipe

def send(pipe):
    # 要执行的子进程
    pipe.send("This is a message.")
    pipe.close()


if __name__ == "__main__":
    (send_pipe, recv_pipe) = Pipe()
    # 实例化进程
    p = Process(target=send, args=(send_pipe, ))
    # 执行子进程
    p.start
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值