Python多线程并行学习2

上篇文章说到,由于Python多线程 multiprocessing 模块中在windows 平台和 Unix 平台系统的底层实现方法不同,所以效率和内存占用会有差别,本文继续记录如何在不同平台实现多线程并行。

Contents

  1. 基础的多进程调用函数和模块。
  2. 在类的成员函数中调用多进程。
  3. 在类的成员函数中调用多进程遇到的一些问题。

如何调用多进程函数

此处以multiprocessing.Pool为例:

file1.py

def target_fun():
	return 1
	

main function:

import multiprocessing
from file1 import target_fun

if __name__ == '__main__':
	pool = multiprocessing.Pool(N)
	# N为pool的数目,如果空缺则pool的数目为cpu的数目
	res = pool.starmap(target_fun, [[] for _ in range(10)])
	# pool.starmap 比 pool.map的优点在于,可以输入0~任意个输入参数,而pool.map只有一个参数。后面参数的个数控制了有多少个排队进入pool的子进程
	pool.close()
	# pool关闭,不再接受新的进程,已有的进程结束后自动退出。
	pool.join()
	# 父进程在等子进程结束

在类的成员函数中调用multiprocessing.Pool()

class_define.py:

class MC:
	def __init__(self):
		self.nstep = 30000
		# 总计算30000步
		
	def MCstep(self):
		self.nstep = self.nstep//10
		# 每一个子进程算3000步
		do_chunk()
		# 搞事情
		return res
		# 返回结果
		
	def MCseries(self):
		pool = multiprocessing.Pool(10)
		res_list = pool.starmap(self.MCstep, [[] for _ in range(10)])
		# 创建10个子进程,返回并收集结果。
		pool.close()
		pool.join()
	

main_fun.py

from class_define import MC

mc_test = MC()
# 实例化类

if __name__ == '__main__':
# 只允许直接调用本函数的进程执行下面的操作,防止子进程循环调用迭代
	mc_test.MCseries()
	# 调用实例的成员函数

可能的报错

  1. TypeError: cannot serialize ‘_io.TextIOWrapper’ object

原因:MC类中包含不能被pickled 的成员变量,例如:Files object。导致在实例的成员函数中创建子进程时,无法把当前的实例中的所有成员pickle并复制给子进程。

解决方法:pickle前删掉Files object,在子进程中再重新创建对象。代码:

class_define2.py:

class MC:
	def __init__(self):
		self.nstep = 30000
		# 总计算30000步
		self.f = open('haha.txt', 'w')
		
	def __getstate__(self):
		"""从字典中删掉self.f"""
		state = self.__dict__.copy()
		del state['f']
		return state
		
	def __setstate__(self, state):
		"""Called while unpickling."""
		self.__dict__.update(state)
	
	def MCstep(self):
		self.f = open('haha.txt', 'w')
		"""重新创建文件对象"""
		self.nstep = self.nstep//10
		# 每一个子进程算3000步
		do_chunk()
		# 搞事情
		return res
		# 返回结果
		
	def MCseries(self):
		pool = multiprocessing.Pool(10)
		res_list = pool.starmap(self.MCstep, [[] for _ in range(10)])
		# 创建10个子进程,返回并收集结果。
		pool.close()
		pool.join()
	

问题参考,文档,pickle详解

  1. 子进程产生的随机结果相同

原因:Windows和Linux创建子进程的过程不完全相同,当Windows创建子进程时,会拷贝并初始化一系列父进程中的变量和对象;而在Linux中,fork出的子进程会完全继承父进程的一系列变量,只有当这些变量更改时才会重新复制出专属于子进程的变量和对象。

同样的道理,再windows中,子进程的random seed会重新初始化,并利用创建的时间作为子进程的随机种子;而在Linux中,子进程完全继承父进程的随机种子,导致所有的子进程都拥有一样的随机种子,所有的random, numpy, scipy 都产生一模一样的随机数。

解决的方法:在子进程中重新调用random.seed() 或 np.random.seed(),并祈祷子进程的创建不要太快(否则导致系统时间相同)

class_define2.py:

class MC:
	def __init__(self):
		self.nstep = 30000
		# 总计算30000步
		self.f = open('haha.txt', 'w')
		
	def __getstate__(self):
		# 从字典中删掉self.f
		state = self.__dict__.copy()
		del state['f']
		return state
		
	def __setstate__(self, state):
		# Called while unpickling.
		self.__dict__.update(state)
	
	def MCstep(self):
		random.seed()
		"""初始化随机种子"""
		self.f = open('haha.txt', 'w')
		# 重新创建文件对象
		self.nstep = self.nstep//10
		# 每一个子进程算3000步
		do_chunk()
		# 搞事情
		return res
		# 返回结果
		
	def MCseries(self):
		pool = multiprocessing.Pool(10)
		res_list = pool.starmap(self.MCstep, [[] for _ in range(10)])
		# 创建10个子进程,返回并收集结果。
		pool.close()
		pool.join()
	

问题参考,文档参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值