Manager()返回一个manager对象,它控制一个服务器进程,这个进程会管理python对象并允许其他进程通过代理的方式来操作这些对象
manager对象支持多对种类型。例子见下
from mulitprocessing 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,arge=(d,1))
p.start()
p.join()
print(d)
print(l)
with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会这执行必要的 ‘清理’ 操作,释放资源,比如文件使用后自动关闭,线程中锁的自动获取acquire和release等
with 语句的实现类似try..finally
服务器进程管理器比使用共享内存对象更灵活,它们支持二进制对象类型,同时,一个单独的manager可以被网上的不同计算器的进程共享,缺点是比使用shared memory慢
managers 提供了创建一种数据的方法,这个数据可以被不同的进程共享,这种共享也包括通过网络在不同计算器的进程上共享
multiprocessing.Manager()
返回一个已启动的SyncManager对象(BaseManager的子例的实例对象),用于在进程之间分析数据
SyncManager对应一个已经启动的子进程,它拥有一系列方法,可以为大部分常用数据类型创建并返回代理对象,用于进程间同步,甚至包括共享列表和字典
当管理器被垃圾回收或者父进程退出时,管理进程会立即退出
class mulitprocessing.managers.BaseManager([adderss[,authkey]])
创建一个BaseManager对象,创建后,需要调用start()或get_server().server_forever()确保对象对于的管理进程已经启动。
start() 为管理器开启子进程
get_server()返回一个Server对象
connect()连接本地管理器对象到一个远程管理器进程
shutdown()停止管理器的进程,配置start()
register(typid,callable)最重要的类方法,凡是注册到管理器的类型/对象,都可以被网络上的不同进程共享了
下面是一个简单的Master/Worker模型,实现一个简单的分布计算,如果要启动多个worker,就可以把任务分配到多台机器上了,比如把计算n*n的代码替换成发送邮件时,就实现了邮件队列的异步发送
通过manager模块的支持,多进程分布到多台机器上,一个服务进程可以作为调度者,将任务分布到其他多个进程
注意Queue的作用是用来传递任务和接受结果,每个任务的描述数据量要尽量小
比如发送一个处理日志文件的任务,就不要发送几百兆的日志文件本身,而是发送日志文件存放的完整路径,由worker进程再去共享的磁盘上读取文件
会报两个错误
第一个Ⅹ
#_pickle.PicklingError: Can't pickle at 0x107ef8670>: attribute lookup on __main__ failed
网上查了下pickle模块不能使用lambda函数,被封存对象不能是lambda函数返回的对象,只能是def定义返回的对象
第二个Ⅹ
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
看文档3.8版本增加了freeze_support()函数,主要是为了支持windows可执行文件,毕竟mulitprocessing可用于分布式进程,所以必须引入freeze_support
看代码:
服务器上的代码:
import random, time, queue
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support
# 建立2个队列,一个发送,一个接收
task_queue = queue.Queue()
result_queue = queue.Queue()
def get_task():
return task_queue
def get_result():
return result_queue
class QueueManager(BaseManager): pass
# 服务器的管理器上注册2个共享队列
QueueManager.register('get_task', callable=get_task)
QueueManager.register('get_result', callable=get_result)
# 设置端口,地址默认为空。验证码authkey需要设定。
manager = QueueManager(address=('127.0.0.1', 5000), authkey=b'abc')
def manager_run():
manager.start()
# 通过管理器访问共享队列。
task = manager.get_task()
result = manager.get_result()
#对队列进行操作, 往task队列放进任务。
for value in range(10):
n = random.randint(0,100)
print('Put task %d' % n)
task.put(n)
# 从result队列取出结果
print('Try get result...')
try:
for value in range(10):
r = result.get(timeout=10)
print('Result: %s' % r)
except queue.Empty:
print('result is empty')
# 关闭管理器。
manager.shutdown()
print('master exit.')
if __name__ == '__main__':
freeze_support()
manager_run()
令一台机器或本地启动也可以
import time, sys, queue
from multiprocessing.managers import BaseManager
class QueueManager(BaseManager): pass
# 从网络上的服务器上获取Queue,所以注册时只提供服务器上管理器注册的队列的名字:
QueueManager.register('get_task')
QueueManager.register('get_result')
server_addr = '127.0.0.1'
print('Connect to server %s...' % server_addr)
# b'abc'相当于'abc'.encode('ascii'),类型是bytes
m = QueueManager(address=(server_addr, 5000), authkey=b'abc')
# 连接服务器
m.connect()
# 获得服务器上的队列对象
task = m.get_task()
result = m.get_result()
for value in range(10):
try:
n = task.get(timeout=1)
print('run task %d * %d...' % (n, n))
r = '%d * %d = %d' % (n , n, n*n)
time.sleep(1)
result.put(r)
except queue.Empty:
print('task queue is empty')
print('worker exit.')