一直在学习,跟着雪峰老师的讲解,跟着《Python爬虫开发与项目实战》,跟着笨办法学Python(不得不说确实是新手上路的一本好书),跟着CSDN里的大神,跟着慕课网的老师,跟着一切能跟着资源......只是今天才开始记录自己的学习历程,之前的会慢慢补齐,今天就从“分布式进程”开始记录吧
话不多少,先上今天的学习内容
# task_master.py
import random, time, queue #注释1
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support #注释2
# 进程个数
task_number = 10
# 自定义发送任务的队列:
task_queue = queue.Queue(task_number)
# 自定义接收结果的队列:
result_queue = queue.Queue(task_number)
def get_task(): #注释3,包括下面3
return task_queue
def get_result():
return result_queue
# 从BaseManager继承的QueueManager:
class QueueManager(BaseManager):
pass
def win_run():
# 把两个Queue都注册到网络上, callable参数关联了Queue对象:
QueueManager.register('get_task_queue', callable=get_task) #注释4
QueueManager.register('get_result_queue', callable=get_result) #注释4
# 绑定端口5000, 设置验证码'abc',windows下需要填写IP,Linux下空默认为本地
manager = QueueManager(address=('127.0.0.1', 5000), authkey=b'abc') #注释5
# 启动Queue:
manager.start()
try:
# 获得通过网络访问的Queue对象:
task = manager.get_task_queue()
result = manager.get_result_queue()
# 放几个任务进去:
for i in range(10):
n = random.randint(0, 10000)
print('Put task %d...' % n)
task.put(n) #注释6
# 从result队列读取结果:
print('Try get results...')
for i in range(10):
r = result.get(timeout=10)
print('Result: %s' % r)
except:
print ('Manager Error')
finally:
# 关闭:
manager.shutdown()
print('master exit.')
if __name__ == "__main__":
freeze_support() #注释2
win_run()
熟悉的都应该知道这是雪峰老师python3中所讲到的知识,只是里面有几处我私自改动的地方,也是掉进雪峰老师那个坑后自己慢慢爬出来的一个个脚印
注释1:Py2和Py3的差异,3中import的queue是小写而不是大写、
注释2:这里是为了防止过多的进程和窗口导致程序出错而设置的一个预防手段,只不过在涉及到进程池的时候可能会用到,向上面这样的小程序是基本上不可能用到的,写在这里只是为了加深印象
注释3:这里的四行代码是为了函数win_run中
QueueManager.register('get_task_queue', callable=get_task)
QueueManager.register('get_result_queue', callable=get_result)
这两句话而提前设置的,雪峰老师的代码中,这部分所用到的是Linux环境下的参数,和Windows不同的是,Linux环境下有个叫“lambda”的参数,可以自动绑定所调用的接口,所以不需要自行定义,但是Windows由于没有这个参数,所以只能先将需要调用的接口定义好
注释4:将上面自定义的两个函数作为对象关联到队列中,也就是这里,Windows不能使用lambda参数
注释5:这里的authkey参数后面跟的字符串一定要进行编码!!!一定要进行编码!!!否则就会报编码错误
manager = QueueManager(address=('127.0.0.1', 5000), authkey=b'abc')
注释6:这里是自己犯的一个小错误,当时并没有将put放到for循环当中,导致最后worker.py接收到的队列中只有master.py中的最后一条数据,写在这里算是给自己提个醒
下面这一部分是worker.py,用于接受上面master在网络中所传送的队列并进行处理,最后将结果返回到master中
这一部分中的注释已经写的十分详细了,不需要在做过多的说明:
import time, queue
from multiprocessing.managers import BaseManager
#创建类似的QueueManager
class QueueManager(BaseManager):
pass
#由于QueueManager只是从网络上获取Queue,所以注册时只需要提供名字
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')
#链接到服务器,也就是运行task_queue的机器
server_addr = '127.0.0.1'
print ('Connect to server %s ...' % server_addr)
#端口和验证码注意和master.py中设置的要完全一致
m = QueueManager(address=(server_addr, 5000), authkey=b'abc')
#从网络连接
m.connect()
#获取queue的对象
task = m.get_task_queue()
result = m.get_result_queue()
#从task队列中去除任务,并将结果存入到result队列中
for i 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.')
这里补充一下python中queue的相关知识点:
Python queue模块有三种队列及构造函数:
1、Python queue模块的FIFO队列先进先出。 class queue.queue(maxsize)
2、LIFO类似于堆,即先进后出。 class queue.Lifoqueue(maxsize)
3、还有一种是优先级队列级别越低越先出来。 class queue.Priorityqueue(maxsize)
此包中的常用方法(q =queue.queue()):
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
非阻塞 q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作