多进程

(一)生成进程

通过父进程来创建进程,父进程会以异步的形式继续执行,或是等待,直到子进程执行结束为止。Python的multiprocessing库可以通过如下步骤来生成进程:

  1. 构建对象进程;
  2. 调用其start()方法。该方法会开启进程活动;
  3. 调用其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的可能值有如下及项:

  1. exitcode == 0:表示没有错误;
  2. exitcode > 0:表示进程遇到了错误并退出;
  3. exitcode < 0:表示进程被信号 -1 * ExitCode 杀死了。

(四)创建自定义进程

方法:

  1. 定义一个Process 类的子类;
  2. 重写__init__(self [,args])方法增加额外的参数;
  3. 重写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()

(六)同步进程

每个进程可以协同工作来执行一个给定的任务。通常情况下,它们会共享数据。多个进程对于共享数据的访问不会产生不一致的数据是很重要的。因此,通过共享数据进行协作的进程必须要按照一定的顺序来访问数据。

  1. Lock:该对象可以处于上锁与未上锁状态。锁对象有两个方法:acquire()与release(),用于管理对共享资源的访问。
  2. Event(事件):它实现了进程间的简单通信,一个进程会发出事件,其他进程会等待事件。Event对象有两个方法:set()和clear(),用于管理内部的标志。
  3. 条件:该对象用于同步串行或是并行进程中的部分工作流。它有两个基本的方法,wait()用于等待条件,而notify_all()则用于与所应用的条件进行通信。
  4. 信号量:用于共享公共资源,比如,支持固定数量的同时连接。
  5. RLock:定义了递归的lock对象。RLock的方法和功能与threading模块中的一样。
  6. 屏障:将一个程序划分为几个阶段,因为它要求所有进程都到达后才能开始执行。屏障后的代码不能与屏障前的代码并发执行。

例:

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对象,并可以让其他进程操作这些对象。
管理器有如下属性:

  1. 它会控制服务端进程,该进程会管理共享对象;
  2. 当有人修改共享对象时,它会确保共享对象在所有进程中都会更新。

例:

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类拥有如下方法:

  1. apply():一直会阻塞,直到结果就绪为止。
  2. apply_async():返回一个结果对象。它是一个异步操作,并不会锁定主线程,直到所有子类都执行完毕为止。
  3. map():这是内建的map()函数的并行版本。它会阻塞住,直到结果就绪为止。该方法会将迭代的数据以块的形式提交给进程池,并作为单独的任务来执行。
  4. 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))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值