协程
协程(coroutine),又称微线程,纤程,一种用户级的轻量级线程。对于协程来说,拥有自己的寄存器上下文和栈,在协程调用切换时,将寄存器上下文和栈保存到其他地方,在之后切换回来的时候,恢复之前保存的寄存器上下文和栈。也就是说协程能够保留上一次调用的状态。与线程的不同在于线程是系统级别的,由操作系统调度;协程是程序级别的,由程序员在程序中根据需要自行调度。
- 使用yield实现协程
# -*- coding: utf-8 -*-
def run_func():
print("函数开始执行")
num = 0
for i in range(5):
recv = yield num
print("我收到了数据 %s" % recv)
print("方法中num=%s" % num)
num += 10
if __name__ == '__main__':
run_func = run_func()
print(type(run_func))
# __next()__等同于 send()方法,返回目前yield后面表达式的值
num_tmp = run_func.__next__()
print("目前num_tmp=%s" %num_tmp)
num_tmp = run_func.send(520)
print("目前num_tmp=%s" %num_tmp)
- 使用greenlet实现协程
# -*- coding: utf-8 -*-
from greenlet import greenlet
def run_task1():
print("方法1...520")
gtask2.switch() # 手动切换去执行run_task2
print("方法1,我回来了")
gtask2.switch() # 手动切换回run_task2之前执行的位置
def run_task2():
print("方法2...520")
gtask1.switch() # 手动切换回run_task1之前执行的位置
print("方法2,我回来了")
if __name__ == '__main__':
gtask1 = greenlet(run=run_task1) # 创建协程task1
gtask2 = greenlet(run=run_task2) # 创建协程task2
gtask1.switch() # 跳转至协程gtask1
- 使用gevent实现协程
# -*- coding: utf-8 -*-
from gevent import monkey, spawn, joinall
def run_task(num):
print("得到数字%s, 平方为%s" %(num, pow(num, 2)))
if __name__=='__main__':
monkey.patch_all() # 遇到阻塞时自动切换协程
nums = [i for i in range(5)]
# 定义协程方法
greenlets = [spawn(run_task, num) for num in nums]
# 添加协程任务并且启动运行
joinall(greenlets)
在gevent中使用进程池
# -*- coding: utf-8 -*-
from gevent import monkey, spawn, joinall, pool
monkey.patch_all() # 遇到阻塞时自动切换协程
def run_task(num):
print("得到数字%s, 平方为%s" %(num, pow(num, 2)))
if __name__=='__main__':
pool = pool.Pool(2)
nums = [i for i in range(5)]
# 进程池的map()函数将第二个参数中的值一个个传入第一个参数表示函数所需要的参数中
pool.map(run_task, nums)
分布式进程
分布式进程是将Process进程分布到多台机器上,充分利用多台机器的性能完成复杂的任务。
distributed_manager.py
# -*- coding: utf-8 -*-
from multiprocessing.managers import BaseManager
from multiprocessing import freeze_support, Queue
task_number = 5
# 服务进程创建任务队列,作为传递任务给任务进程的通道
task_queue = Queue(task_number)
# 服务进程创建结果队列,作为任务进程完成任务后回复服务进程的通道
result_queue = Queue(task_number)
# win7 64 貌似不支持callable下调用匿名函数lambda,这里封装一下
def get_task():
return task_queue
def get_result():
return result_queue
class QueueManager(BaseManager):
pass
def run_task():
# QueueManager.register('get_task_queue', callable=lambda:task_queue)
# QueueManager.register('get_task_queue', callable=lambda:result_queue)
# 将两个队列注册到网上,callable参数关联Queue对象
QueueManager.register('get_task_queue', callable=get_task)
QueueManager.register('get_result_queue', callable=get_result)
# 绑定端口,设置验证码
manager = QueueManager(address=('127.0.0.1', 8001), authkey='520'.encode())
# 启动
manager.start()
try:
# 获得通过网络访问的Queue对象
task = manager.get_task_queue()
result = manager.get_result_queue()
# 放任务进去
for num in ["num " + str(i) for i in range(task_number)]:
print("放入数据 %s" % num)
task.put(num)
# 从结果队列result中读取结果
print("正在尝试读取结果")
for i in range(task_number):
print("结果是 %s" % result.get(timeout=5))
except:
print("服务进程错误")
finally:
# 关闭
manager.shutdown()
print("服务进程关闭...")
if __name__ == '__main__':
# window下多进程可能有问题
freeze_support()
run_task()
distributed_worker.py
# -*- coding: utf-8 -*-
from multiprocessing.managers import BaseManager
import time
class QueueManager(BaseManager):
pass
if __name__ == '__main__':
# 在任务进程中QueueManager只从网络上获取Queue,所以注册时只提供名字即可
QueueManager.register('get_task_queue')
QueueManager.register('get_result_queue')
# 连接服务器,也就是运行服务进程的机器
server_addr = '127.0.0.1'
print("正在连接服务器 %s..." % server_addr)
# 端口和验证码需要正确
manager = QueueManager(address=(server_addr, 8001), authkey='520'.encode())
manager.connect()
# 获取网络中的Queue
task = manager.get_task_queue()
result = manager.get_result_queue()
# 从task任务队列中获取任务,将结果写入result结果队列
while (not task.empty()):
num = task.get(True, timeout=5)
print("执行任务数据 %s..." % num)
time.sleep(1)
result.put('num %s' % pow(int(num[4]), 2))
print("任务进程结束...")