多进程之子进程与父进程关系
from multiprocessing import Pool, Process
import time
import os
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id', os.getpid())
def f(name):
time.sleep(5)
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
p = Process(target=f, args=('Bob',))
p.daemon = True # 设置为守护进程
p.start()
p.join()
# 当进程不使用join时,如果p主进程为守护进程,那么f子进程可能还没执行完,p父进程就退出了
# f可能会变成僵尸进程(孤儿), 所以如果希望父进程等待子进程执行完任务再退出,则需要使用join
# 当p主进程不是守护进程时,则p不需要join也会等待子进程执行完才退出
# 为了通常不管需不需要设为守护进程,我们都希望子进程能完成任务,那么每次新建进程时都使用join吧
多进程之自定义创建进程方式
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()
# 多进程支持多种创建方式spawn,fork, forkserver
# spawn 支持unix和windows,这个方式在windows是默认的创建方式,这个方法效率低于fork和forkserver
# fork 仅支持unix,unix平台默认创建方式为fork,
# forkserver 支持能在unix平台传递文件描述符的管道平台
# 不同的创建进程方式之间是不兼容的,特别是锁创建,使用fork上下文,是不能启动使用spawn或forkserver方式创建的进程
多进程之Queue队列实现进程间数据共享
from multiprocessing import Pool, 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()) # 父进程获取子进程的数据
p.join()
# 进程间的数据是不共享的,如果需要交换数据,需要使用进程模块的Queue队列方法
# 队列是线程和进程安全的
多进程之Pipe管道实现进程间数据通信
from multiprocessing import Pool, Process, Queue, Pipe
def f(conn):
conn.send([42, None, 'hello']) # 子进程通过管道向管道发生数据
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe() # 获取一对管道
p = Process(target=f, args=(child_conn,)) # 将其中一个管道传给子进程
p.start()
print(parent_conn.recv()) # 使用另一个管道接受数据
p.join()
# 通过管道也能实现进程间数据通信
多进程之Lock实现数据同步
from multiprocessing import Pool, Process, Queue, Pipe, Lock
def f(l, i):
l.acquire() # 加锁后,其他进程将被阻塞,只有最先调用acquire获得锁的进程才能执行
try:
print('hello world', i)
finally:
l.release() # 调用release释放锁后,其他进程将又会抢占资源
if __name__ == '__main__':
lock = Lock()
for num in range(10):
Process(target=f, args=(lock, num)).start() # 由于进程创建后就立即执行,所以该例子最终变成串行方式
# 通过加锁使进程间对于同一个资源的使用保持同步,避免脏数据,加锁后,得到锁的进程会锁定资源,其他进程将进入阻塞状态,直到锁被释放,所有进程又将开始抢占资源,直到某一个进程又获得了锁,这时其他没有获得锁的进程又被阻塞
多进程之进程间数据状态共享
from multiprocessing import Pool, Process, Queue, Pipe, Lock, Value, Array
def f(n, a):
n.value = 3.1415927
for i in range(len(a)): # 子进程对父进程传进来的数据进行修改
a[i] = -a[i]
if __name__ == '__main__':
num = Value('d', 0.0) # 第一个参数为数据类型,d为double双精度浮点型,适合单个值状态共享
arr = Array('i', range(10)) # 第一个参数是数据的类型,i为整型,适合多个值状态共享
p = Process(target=f, args=(num, arr))
p.start()
p.join()
print(num.value)
print(arr[:]) # 父进程访问子访问子进程操作过的数据,这个数据通常讲,进程间的数据是独立的,但是通过这种方式,实现了进程间的数据也能实现状态共享,这里的数组使用[:]分片表示将数据拷贝了一份
# 上面进程间同步时需要我们手动加锁,避免进程对同一个资源同时修改,我们也可以使用Value或Array两个方法,实现进程间的数据状态共享,其内部也是通过加锁方式避免了脏数据产生
多进程之Manager代理,实现进程间数据共享
from multiprocessing import Pool, Process, Queue, Pipe, Lock, Value, Array, 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() # 由manager代理的python 字典对象
l = manager.list(range(10)) # 由manager代理的python列表对象
p = Process(target=f, args=(d, l)) # 由子进程进行操作代理的数据
p.start()
p.join()
print(d) # 父进程访问数据时与子进程数据保持同步
print(l)
# 通过Manager来代理进程间操作python对象,这样也能实现进程间数据共享。
#Manager支持代理的对象list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value