multiprocessing模块实现多进程

本文详细介绍了Python中的多进程操作,包括通过`multiprocessing`模块的`Pool`和`Process`类创建进程,以及设置启动方法。还探讨了进程间的数据共享,如使用`Queue`和`Pipe`进行通信,以及同步原语如`Lock`。此外,还讨论了使用`Manager`实现更灵活的数据共享,并提供了多个示例来说明这些概念。
摘要由CSDN通过智能技术生成

1.简单实例

通过pool进程池创

from multiprocessing import Pool
def f(x):
    return x*x
if __name__=='__main__':
    with Pool(5) as p: #进程池中创建5个进程,去执行f
        print(p.map(f,[1,2,3]))    

通过Process类创建,创建一个Process类,然后调用它的start()方法来生成启动进程,通过Thread类创建线程

from multiprocessing import Process
def info(name):
    print('hello',name)
    print('module name:',__name__)
    #取得父进程ID
    print('parent process:',os.getppid())
    #取得本进程ID
    print('process id:',os.getpid())
def f(name):
    info('function f')    
    print('f function hello')
if __name__=='__main__':
    info('main line')
    p=Process(targer=f,args=('bob',))    
    p.start()
    p.join()
执行结果
hello main line
module name: __main__
parent process: 10276
process id: 13888
hello function f
module name: __mp_main__
parent process: 13888
process id: 5820
f function hello    

2.通过上下文启动的方法

根据不同的平台有三种启动方法
spawn:支持window和liunx,启动进程相当慢
fork:只能在liunx上使用,windows上没有此方法
forkserver:服务器进程,只能在liunx上使用,启动一个服务器进程,每当需要一个新W进程时,父进程就会连接到服务器并请求它分叉一个新进程。分叉服务器进程是单线程的,因此使用 os.fork() 是安全的
使用set_start_method()来设置进程的启动方法,不能多次调用

import multiprocessing as mp

def foo(q):
    q.put('hello')

if __name__ == '__main__':
    #设置启动方法为spawn
    mp.set_start_method('spawn')
    q = mp.Queue()
    p = mp.Process(target=foo, args=(q,))
    p.start()
    print(q.get())
    p.join()

取得上下文对象使用get_context(),上下文对象与多进程对象有相同的api,并允许在同一程序中使用多种启动方法

import multiprocessing as mp

def foo(q):
    q.put('hello')

if __name__ == '__main__':
    ctx = mp.get_context('spawn')
    q = ctx.Queue()
    p = ctx.Process(target=foo, args=(q,))
    p.start()
    print(q.get())
    p.join()

3.多进程中共享数据(交换对象)

支持两种通信通道,队列Queue和管道Pipe

3.1 队列Queue(线程和进程安全)

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print(q.get())    # prints "[42, None, 'hello']"
    p.join()

队列中的数据不能重复使用,必须按顺序执行put和get,即只有当队列中有数据时才能get,无数据时,get方法无法取得值,且不会发生异常

3.2 管道Pipe,同队列,只有管道内有数据才能被recv方法读取到数据

from multiprocessing import Process, Pipe

def f(conn):
    conn.send([42, None, 'hello'])
    conn.close()

if __name__ == '__main__':
    #Pipe可以传参数True of False,
    #为True时,管道为双向,两个对象都可以用来接收和发送数据,默认值
   #为False时,管道为单向,parent_conn只能接收数据,child_conn只能发送消息 
    parent_conn, child_conn = Pipe()
    p = Process(target=f, args=(child_conn,))
    p.start()
    print(parent_conn.recv())   # prints "[42, None, 'hello']"
    p.join()

返回的两个连接对象 Pipe() 表示管道的两端。每个连接对象都有 send() 和 recv() 方法(相互之间的)。请注意,如果两个进程(或线程)同时尝试读取或写入管道的 同一 端,则管道中的数据可能会损坏。当然,在不同进程中同时使用管道的不同端的情况下不存在损坏的风险。

4.进程间的同步

对于所有在 threading 存在的同步原语,multiprocessing 中都有类似的等价物。例如,可以使用锁来确保一次只有一个进程打印到标准输出:

from multiprocessing import Process, Lock

def f(l, i):
    #启动锁
    l.acquire()
    try:
        print('hello world', i)
    finally:
        #解锁
        l.release()

if __name__ == '__main__':
    #创建锁对象
    lock = Lock()

    for num in range(10):
        #锁传入子进程
        Process(target=f, args=(lock, num)).start()

5.进程间共享状态(尽量避免这种做法)

使用Value或Array类将数据存储共享内存映射中

from multiprocessing import Process, Value, Array

def f(n, a):
    n.value = 3.1415927
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    #此处的d和if都为ctypes对象
    num = Value('d', 0.0)
    arr = Array('i', range(10))

    p = Process(target=f, args=(num, arr))
    p.start()
    p.join()

    print(num.value)
    print(arr[:])
执行结果
3.1415927
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]    

使服务进程Manager()共享
Manager可以共享的类型有

list
dict
Namespace
Lock
RLock
Semaphore
BoundedSemaphore
Condition
Event
Barrier
Queue
Value
Array

简单实例,

from multiprocessing import Process, Manager

def f(d, l):
    d[1] = '1'
    d['2'] = 2
    d[0.25] = None
    l.reverse()

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list(range(10))

        p = Process(target=f, args=(d, l))
        p.start()
        p.join()

        print(d)
        print(l)
        
{0.25: None, 1: '1', '2': 2}
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

使用服务进程的管理器比使用共享内存对象更灵活,因为它们可以支持任意对象类型。此外,单个管理器可以通过网络由不同计算机上的进程共享。但是,它们比使用共享内存慢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值