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