(一)生成进程
通过父进程来创建进程,父进程会以异步的形式继续执行,或是等待,直到子进程执行结束为止。Python的multiprocessing库可以通过如下步骤来生成进程:
- 构建对象进程;
- 调用其start()方法。该方法会开启进程活动;
- 调用其join()方法。它会等待,直到进程完成其工作并退出为止。
例:
import multiprocessing
def foo(i):
print('called function in process: %s' % i)
return
if __name__ == '__main__':
process_jobs = []
for i in range(5):
p = multiprocessing.Process(target = foo, args = (i,))
process_jobs.append(p)
p.start()
p.join()
(二)在后台运行进程
在后台运行进程是一种典型的耗时处理执行模式,它不需要你的介入与干预,当然也可以与其他程序并发执行。Python的multiprocessing模块通过daemon选项可以实现进程的后台运行。
例:
import multiprocessing
import time
def foo():
name = multiprocessing.current_process().name
print('Starting %s' % name)
time.sleep(2)
print('Exiting %s' % name)
if __name__ == '__main__':
background_process = multiprocessing.Process(name = 'background_process', target = foo)
background_process.daemon = True
no_background_process = multiprocessing.Process(name = 'no_background_process', target = foo)
no_background_process.daemon = False
background_process.start()
no_background_process.start()
注意:后台进程是不允许创建子进程的。否则,当后台进程的父进程退出时,它会终止,这会导致这个后台进程的子进程变成游离状态。此外,它们并非UNIX守护进程或服务,它们就是正常的进程,如果非后台进程退出时,它们就会终止。
(三)杀死进程
通过terminate()方法立刻杀死一个进程。此外,还可以通过is_alive()方法来追踪进程是否存活。
例:
import multiprocessing
import time
def foo():
print('Starting function')
time.sleep(0.1)
print('Finishing function')
if __name__ == '__main__':
p = multiprocessing.Process(target = foo)
print('Process before execution:', p, p.is_alive())
p.start()
print('Process running:', p, p.is_alive())
p.terminate()
print('Process terminateed:', p, p.is_alive())
p.join()
print('Process joined:', p, p.is_alive())
print('Process exitCode:', p.exitcode)
exitcode的可能值有如下及项:
- exitcode == 0:表示没有错误;
- exitcode > 0:表示进程遇到了错误并退出;
- exitcode < 0:表示进程被信号 -1 * ExitCode 杀死了。
(四)创建自定义进程
方法:
- 定义一个Process 类的子类;
- 重写__init__(self [,args])方法增加额外的参数;
- 重写run(self [,args])方法实现Process 启动后需要做的事情。
创建好新的Process子类后,可以创建它的一个实例,然后通过调用start()方法来启动,该方法又会调用run()方法。
例:
import multiprocessing
import time
class MyProcess(multiprocessing.Process):
def run(self):
print('called run method in process: %s' % self.name)
if __name__ == '__main__':
jobs = []
for i in range(5):
p = MyProcess()
jobs.append(p)
p.start()
p.join()
(五)在进程间交换对象
并行应用的开发需要在进程间进行数据交换。multiprocessing库有两个通信通道:队列与管道。
使用队列进行对象交换:队列会返回一个进程共享队列,它是线程与进程安全的,任何可序列化对象都可以通过它进行交换。
例1:使用队列进行对象交换
from multiprocessing import Process, Queue
import random
import time
class Producer(Process):
def __init__(self, queue):
Process.__init__(self)
self.queue = queue
def run(self):
for i in range(10):
item = random.randint(0, 256)
self.queue.put(item)
print('Process Producer: item %d appended to queue %s' % (item, self.name))
time.sleep(1)
print('The size of queue: %s' % self.queue.qsize())
class Consumer(Process):
def __init__(self, queue):
Process.__init__(self)
self.queue = queue
def run(self):
while True:
if self.queue.empty():
print('the queue is empty')
break
else:
time.sleep(2)
item = self.queue.get()
print('Process Consumer: item %d poped from %s' % (item, self.name))
time.sleep(1)
if __name__ == '__main__':
queue = Queue()
process_producer = Producer(queue)
process_consumer = Consumer(queue)
process_producer.start()
process_consumer.start()
process_producer.join()
process_consumer.join()
(六)同步进程
每个进程可以协同工作来执行一个给定的任务。通常情况下,它们会共享数据。多个进程对于共享数据的访问不会产生不一致的数据是很重要的。因此,通过共享数据进行协作的进程必须要按照一定的顺序来访问数据。
- Lock:该对象可以处于上锁与未上锁状态。锁对象有两个方法:acquire()与release(),用于管理对共享资源的访问。
- Event(事件):它实现了进程间的简单通信,一个进程会发出事件,其他进程会等待事件。Event对象有两个方法:set()和clear(),用于管理内部的标志。
- 条件:该对象用于同步串行或是并行进程中的部分工作流。它有两个基本的方法,wait()用于等待条件,而notify_all()则用于与所应用的条件进行通信。
- 信号量:用于共享公共资源,比如,支持固定数量的同时连接。
- RLock:定义了递归的lock对象。RLock的方法和功能与threading模块中的一样。
- 屏障:将一个程序划分为几个阶段,因为它要求所有进程都到达后才能开始执行。屏障后的代码不能与屏障前的代码并发执行。
例:
from multiprocessing import Process, Barrier, Lock, current_process
from datetime import datetime
from time import time
def test_with_barrier(synchronizer, serializer):
name = current_process().name
synchronizer.wait()
now = time()
with serializer:
print('Process %s -----> %s' % (name, datetime.fromtimestamp(now)))
def test_without_barrier():
name = current_process().name
now = time()
print('Process %s -----> %s' % (name, datetime.fromtimestamp(now)))
if __name__ == '__main__':
synchronizer = Barrier(2)
serializer = Lock()
Process(name = 'p1-test_with_barrier', target = test_with_barrier, args = (synchronizer, serializer)).start()
Process(name = 'p2-test_with_barrier', target = test_with_barrier, args = (synchronizer, serializer)).start()
Process(name = 'p3-test_without_barrier', target = test_without_barrier).start()
Process(name = 'p4-test_without_barrier', target = test_without_barrier).start()
(七)管理进程间状态
Python的multiprocessing模块提供了一种管理器来协调用户之间的共享信息。管理器对象会控制一个服务端进程,该进程持有Python对象,并可以让其他进程操作这些对象。
管理器有如下属性:
- 它会控制服务端进程,该进程会管理共享对象;
- 当有人修改共享对象时,它会确保共享对象在所有进程中都会更新。
例:
import multiprocessing
def worker(dictionary, key, item):
dictionary[key] = item
if __name__ == '__main__':
manager = multiprocessing.Manager()
dictionary = manager.dict()
jobs = [multiprocessing.Process(target = worker, args = (dictionary, i, i * 2)) for i in range(10)]
for j in jobs:
j.start()
for j in jobs:
j.join()
print('results:', dictionary)
(八)使用进程池
multiprocessing库提供了Pool类用于实现简单的并行处理任务。Pool类拥有如下方法:
- apply():一直会阻塞,直到结果就绪为止。
- apply_async():返回一个结果对象。它是一个异步操作,并不会锁定主线程,直到所有子类都执行完毕为止。
- map():这是内建的map()函数的并行版本。它会阻塞住,直到结果就绪为止。该方法会将迭代的数据以块的形式提交给进程池,并作为单独的任务来执行。
- map_async():返回一个结果对象。如果指定了回调,那么它就是可以调用的,并且会接收一个参数。当结果就绪时,回调就会使用到它(除非调用失败了)。回调应该立即完成,否则,处理结果的线程就会被阻塞住。
例:
import multiprocessing
def foo(data):
result = data * data
return result
if __name__ == '__main__':
inputs = list(range(100))
pool = multiprocessing.Pool(processes = 5)
outputs = pool.map(foo, inputs)
pool.close()
pool.join()
print('Pool: ', outputs)
(九)使用mpi4py模块
1.父进程的结束不能影响子进程
from multiprocessing import Process
import time
def run():
print('子进程启动...')
time.sleep(3)
print('子进程启动...')
if __name__ == '__main__':
print('父进程启动...')
p = Process(target=run) #创建进程
p.start() #启动进程
print('父进程结束...')
以上代码运行后输出如下:
C:\pyFile\module\prosess>python process.py
父进程启动...
父进程结束...
子进程启动...
子进程启动...
2.让父进程等待子进程结束后再执行父进程
from multiprocessing import Process
import time
def run():
print('子进程启动...')
time.sleep(3)
print('子进程启动...')
if __name__ == '__main__':
print('父进程启动...')
p = Process(target=run) #创建进程
p.start() #启动进程
p.join() #子进程结束后在执行父进程
print('父进程结束...')
以上代码运行后输出如下:
C:\pyFile\module\prosess>python process.py
父进程启动...
子进程启动...
子进程启动...
父进程结束...
3.全局变量在多个进程之间不能共享
from multiprocessing import Process
import time
#全局变量:
num = 100
def run():
print('子进程启动...')
global num
num += 1
time.sleep(3)
print('子进程结束,num的值为: %d' % num)
if __name__ == '__main__':
print('父进程启动,num的值为: %d' % num)
p = Process(target=run) #创建进程
p.start() #启动进程
p.join() #子进程结束后在执行父进程
print('父进程结束,num的值为: %d' % num)
以上代码运行后输出如下:
C:\pyFile\module\prosess>python process.py
父进程启动,num的值为: 100
子进程启动...
子进程结束,num的值为: 101
父进程结束,num的值为: 100
原因:在创建子进程时对全局变量做了一个备份,父进程中的num与子进程中的num是完全不同的两个变量。
4.使用进程池启动大量子进程
from multiprocessing import Pool
import time, os, random
def run(name):
print('子进程 %d 启动---<%d>' % (name, os.getpid()))
start = time.time()
time.sleep(random.choice([1, 2, 3]))
end = time.time()
print('子进程 %d 结束---<%d>,耗时: %.2f' % (name, os.getpid(), end - start))
if __name__ == '__main__':
print('父进程开始...')
pool = Pool(2) #Pool默认大小是CPU核心数
for i in range(5):
#创建进程,放入进程池统一管理
pool.apply_async(run, args=(i,))
pool.close() #在调用join()之前必须先调用close(),调用close()之后就不能再继续添加新的进程了
pool.join() #进程池对象调用join,会等待进程池中所有的子进程结束后再去执行父进程
print('父进程结束...')
以上代码运行后输出如下:
C:\pyFile\module\prosess>python pool.py
父进程开始...
子进程 0 启动---<4380>
子进程 1 启动---<3372>
子进程 0 结束---<4380>,耗时: 2.00
子进程 2 启动---<4380>
子进程 1 结束---<3372>,耗时: 3.00
子进程 3 启动---<3372>
子进程 2 结束---<4380>,耗时: 3.00
子进程 4 启动---<4380>
子进程 3 结束---<3372>,耗时: 2.00
子进程 4 结束---<4380>,耗时: 2.00
父进程结束...
5.进程间的通信
6.使用多进程实现文件的复制
import os, time
from multiprocessing import Pool
#实现文件的拷贝
def copyFile(rPath, wPath):
fr = open(rPath, 'rb')
fw = open(wPath, 'wb')
context = fr.read()
fw.write(context)
fr.close()
fw.close()
path = r'C:\pyFile\file1'
topath = r'C:\pyFile\file2'
if __name__ == '__main__':
#读取path下的所有文件:
fileList = os.listdir(path)
start = time.time()
pool = Pool()
for fileName in fileList:
pool.apply_async(copyFile, args = (os.path.join(path, fileName), os.path.join(topath, fileName)))
pool.close()
pool.join()
end = time.time()
print('总耗时:%0.2f' % (end - start))