Python学习整理记录之一27.关于协程

1、关于概念定义的收集和整理

迭代器

  • 可迭代(Iterable): 直接作用与for循环的变量
  • 迭代器(Iterator):不但可以作用于for循环,还可以被next调用
  • list是典型的可迭代对象,但不是迭代器

生成器

  • generator: 一边循环一遍计算下一个元素的机制/算法
  • 满足三个条件:
    • 每次调用都能生产出for循环需要的下一个元素
    • 如果达到最后一个,会提示StopIteration异常
    • 可以被next函数调用
  • 如何生成一个生成器
    • 直接使用
    • 如果函数中包含yield,则这个函数就叫生成器
    • next调用函数,遇到yield返回

协程

  • 历史历程
    • 3.4引入协程,用yield实现
    • 3.5引入协程语法
    • 实现的协程比较好的包有asyncio, tornado, gevent
    • 从技术角度讲,协程就是一个你可以暂停执行的函数
    • 协程的实现:
      • yield 返回
      • next|send 调用
    • 协程的四个状态
    • 协程终止
      • 协程中未处理的异常会向上冒泡,传给next函数或者send方法的调用方(即触发协程的对象)
      • 终止协程的一种方式,发送某个哨符值,让协程退出,内置的None和Ellipsis等常量经常用作哨符值
    • yield from
      • 调用协程为了得到返回值,协程必须正常终止
      • 生成器正常终止会发出StopIteration异常,异常对象的value属性保存返回值
      • yield from从内部捕获StopIteration异常
      • 相当于中间的第三方通道
      • 委派生成器
        • 包含yield from表达式的生成器函数
        • 委派生成器在yield from表达式出暂停,调用方可以直接把数据发给子生成器
        • 子生成器再把产出的值发给调用方
        • 子生成器在最后,解释器会抛出StopIteraton,并且把返回值附加到异常对象上

2、函数和方法的收集和整理

asyncio

  • Python3.4开始引入标准库当中,内置对异步IO的支持
  • asyncio本身是一个消息循环
  • 步骤:
    • 创建消息循环
    • 把协程导入
    • 关闭

async , await

  • 为了更好的表示异步IO
  • python3.5引入
  • 使用上进行简单替换
  • 用async替换@asyncio.coroutine
  • 用await替换 yield from
aiohttp
  • asyncio实现单线程的并发IO ,在客户端用处不大
  • 在服务器端可以用asyncio + coroutine配合,因为HTTP是IO操作
  • asyncio实现了TCP,UDP,SSL等协议
  • aiohttp是给予asyncio实现的http框架
  • 安装:pip install aiohttp

concurrent.futures

  • python3新增的库
  • 类似其他语言的线程池的概念
  • 利用mulipprocessing实现真正的并行计算
  • 核心原理:
    • 以子进程的形式,并行多个python解释器,从而令python程序可以利用多核CPU来提升执行速度
    • 由于子进程与主解释器相分离,所以他们的全局解释器锁也是相互独立的
    • 每个子进程都能够完整的使用一个CPU核心
    • concurrent.futures.Executor
      • ThreadPoolExecutor
      • ProcessPoolExecutor
      • 执行的时候需要自行选择
    • submit(func, args, kwargs)
      • func: 异步执行的函数
      • *args,**args[]

concurrent中map函数

  • map(func, *iterables, timeout=None)
    • 跟map函数类似
    • 函数需要异步执行

3、实例的收集和归纳

  • 实例01——asyncio
import threading
# 引入异步IO包
import asyncio
# 使用协程
@asyncio.coroutine
def hello():
    print("Hello world! (%s)" % threading.currentThread())
    print("Start....(%s)" % threading.currentThread())
    yield from asyncio.sleep(10)
    print("Done..... (%s)" % threading.currentThread())
    print("Hello Again (%s)" % threading.currentThread())
    
# 启动消息循环
loop = asyncio.get_event_loop()
# 定义任务
tasks = [hello(), hello()]
# asyncio使用wait等待task执行完毕
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
  • 实例02——
import asyncio
async def wget(host):
    print("wget %s..." % host)
    # 异步请求网址
    connect = asyncio.open_connection(host, 80)
    # 注意yield from的用法
    reader, writer = await connect
    header = "GET / Http/1.0\r\nHost: %s\r\n\r\n" % host
    writer.write(header.encode("utf-8"))
    await writer.drain()
    while True:
        line = await reader.readline()
        # HTTP协议的换行使用\r\n
        if line == b"\r\n":
            break
        print("%s header > %s" % (host, line.decode("utf-8").rstrip()))
    writer.close()
loop = asyncio.get_event_loop()
tasks = [wget(host) for host in ["www.sina.com.cn", "www.163.com", "www.taobao.com"]]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
  • 实例03——aiohttp
import asyncio
from aiohttp import web

async def index(request):
    await asyncio.sleep(0.5)
    return web.Response(body=b'<h1>Index</h1>')

async def hello(request):
    await asyncio.sleep(0.5)
    text = "<h1>hello, %s!</h1>" % request.match_info["name"]
    return web.Response(body=text.encode("utf-8"))

async def init(loop):
    app = web.Application(loop = loop)
    app.router.add_route("GET", "/", index)
    app.router.add_route("GET", "/hello/{name}", hello)
    srv = await loop.create_server(app.make_handler(), "127.0.0.1", 8000)
    print("Server started at http://127.0.0.1:8000....")
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

02 和 03代码在jupyter notebook运行都是失败的,但在pycharm下运行是正常的

  • 实例04——concurrent.futures
import time
from concurrent.futures import ThreadPoolExecutor

def return_future(msg):
    time.sleep(3)
    return msg

# 创建一个线程池
pool = ThreadPoolExecutor(max_workers=2)

# 往线程池加入2个task
f1 = pool.submit(return_future, "hello")
f2 = pool.submit(return_future, "MImi")

# 自带done()方法,等待执行完毕
print(f1.done())
time.sleep(2)
print(f2.done())

print(f1.result())
print(f2.result())
  • 实例05——委派生成器
from collections import namedtuple
#
ResClass = namedtuple("Res", "count average")

# 子生成器
def averager():
    total = 0.0
    count = 0
    average = None

    while True:

        # term相当一个哨符值,当调用send传入的值为None时,终止协程
        term = yield
        if term is None:
            break

        # 子生成器需要完成的计算功能
        total += term
        count += 1
        average = total / count

    return ResClass(count, average)

# 委派生成器
def grouper(storages, key):
    while True:
        # 将client获取averager()返回的值
        storages[key] = yield from averager()

# 客户端代码
def client():
    # 定义两组预处理的数据,以字典形式
    process_data = {
        'b2': [45.5, 89.7, 45.7, 82.4, 34.4, 56, 9],
        'b1': [1.34, 3.45, 6.12, 4.12, 9.01, 0.54]
    }
    storages = {}
    for k, v in process_data.items():
        # 定义协程(委派生成器)
        coroutine = grouper(storages, k)
        # 预激协程
        next(coroutine)
        # 发送数据到协程
        for dt in v:
            coroutine.send(dt)
        # 终止协程
        coroutine.send(None)
    print(storages)

# run
client()

4、总结以及疑惑?心得?

协程是一个相对新的功能模块,也是作为可替代多线程threading的一个选择。
最后课题:由此引发一个联想,关于生产者消费者模型的流程,是否可以加入协程或者以之代替
代码生成中…
网上有一个Tkinter的贪吃蛇案例,其中就是包含了多线程和队列的内容

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值