深入理解Python异步编程

在写代码的时候又遇到一个问题,想要结合asyncio和多线层,结果可能是因为使用python的版本比较低(python3.6.8),不能运行asyncio.run方法,总之就是想要的目的没有达到!

然后开始百度啥的,没找到问题原因及解决办法,最后觉得还是要看一下python异步编程的原理啥的,然后各种搜之后,又被我发现一个大佬写的文章,在这里记录一下文章链接,方便以后看。也给需要的你们安利一下!!!再次感谢大佬,以及希望大佬早日出下篇!!!

深入理解Python异步编程(上)

深入理解Python异步编程(中)

关于上的个人记录

4.4 基于生成器的协程

由于我太菜了,在看上篇中的《4.4 基于生成器的协程》部分时,看不懂,然后就把文章中的代码写下来运行看结果,然后看了好久,再结合大佬的解释,好像才开始看懂了(꒦_꒦) 

开始我通过debug,但是还是不太清楚代码运行过程,最后通过在每个地方输出打印,再结合大佬的说明,最后看懂了,所以一开始看不懂就多看几遍,通过仔细看每一条输出语句最后应该能搞明白

关于生成器的基础,可以看一下我的另一篇:Python 可迭代对象、迭代器、生成器

import socket
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE


selector = DefaultSelector()
# 这里只写一个,方便研究代码运行
urls_todo = [0]
stopped = False


class Future:
    """
    保存异步调用的结果和回调
    """
    def __init__(self):
        print('future __init__')
        self.result = None  # 异步调用的结果
        self._callbacks = []  # 异步调佣的回调函数

    def add_done_callback(self, fn):
        print('future add_done_callback')
        self._callbacks.append(fn)

    def set_result(self, result):
        print('future set_result')
        self.result = result
        for fn in self._callbacks:
            # self 代表Future实例对象
            # 因为这里的回调函数fn都是Task类中的step()
            # 而step()需要传递Future()作为参数
            # 所以这里的self表示的是step中的future
            fn(self)


class Crawler:
    def __init__(self, _url):
        print('crawler __init__')
        self.url = _url
        self.response = b''

    def fetch(self):
        print('crawler fetch')
        sock = socket.socket()
        sock.setblocking(False)  # 设置为不阻塞
        try:
            print('crawler connect')
            sock.connect(('www.baidu.com', 80))
        except BlockingIOError:
            pass

        # f1 存放sock.connect(('www.baidu.com', 80))的结果和回调
        # 即f1是存放socket网络连接异步调用的结果和回调
        # 因为网络连接不需要存储返回的数据,所以在on_connected方法中设置为None,f1.set_result(None)
        f1 = Future()

        # 这里的回调on_connected和下面的on_readable都是实际都是为了调用生成器fetch
        def on_connected():
            print('crawler on_connected, f1=', f1)
            f1.set_result(None)

        # 通过selector.register注册可写事件,当网络连接成功时执行回调on_connected
        selector.register(sock.fileno(), EVENT_WRITE, on_connected)
        print('crawler yield f1, f1=', f1)
        yield f1
        selector.unregister(sock.fileno())

        get = 'GET http://www.baidu.com/ HTTP/1.1'  # 请求地址
        sock.send(get.encode('ascii'))
        print('crawler send socket')
        global stopped
        while True:
            # f2存储服务器返回的结果和返回结果之后的回调
            f2 = Future()

            def on_readable():
                print('crawler on_readable, f2=', f2)
                f2.set_result(sock.recv(1024))

            selector.register(sock.fileno(), EVENT_READ, on_readable)
            print('crawler yield f2 chunk before, f2=', f2)
            chunk = yield f2
            print('crawler yield f2 chunk after, f2=', f2)
            print('crawler chunk=', chunk)
            selector.unregister(sock.fileno())
            if chunk:
                self.response += chunk
            else:
                urls_todo.remove(self.url)
                if not urls_todo:
                    stopped = True
                break


class Task:
    """
    Task 实例对象的作用:
        通过step()启动和恢复生成器的执行
    """
    def __init__(self, coro):
        print('task __init__')
        self.coro = coro  # 生成器
        f = Future()
        f.set_result(None)
        print('task __init__ future=', f)
        # 初始化第一次调用step,用于启动生成器fetch(),即这里的coro
        # 因为第一次启动生成器发送的必须是None,所以这里的f的作用就是提供有None值的Future对象
        self.step(f)

    def step(self, future):
        """
        step方法作用:恢复这个生成器的执行
        """
        print('task step')
        try:
            print('future=', future)
            print('future.result=', future.result)
            # next_future第一次是f1,第二次是f2
            # 如果服务器返回的数据太多,一次性接收不完,会通过fetch中的while True循环不断yield f2来接收
            # 所以next_future会一直是f2直到数据接收完成
            next_future = self.coro.send(future.result)
            print('task nect_future=', next_future)
        except StopIteration:
            return
        # 得到下一次的future,然后给下一次的future添加step()回调。
        # 原来add_done_callback()不是给写爬虫业务逻辑用的
        # 而是用于恢复生成器的执行
        next_future.add_done_callback(self.step)


def loop():
    while not stopped:
        events = selector.select()
        for event_key, event_musk in events:
            callback = event_key.data
            print('loop callback=', callback)
            callback()
            print('--------------------')


if __name__ == '__main__':
    import time
    start = time.time()
    for url in urls_todo:
        crawler = Crawler(url)
        # crawler.fetch() 返回了一个生成器对象,并不是已经调用了fetch函数,只是预激活生成器
        # 真正的调用是在Task的step函数中:self.coro.send(future.result)
        # self.coro就是生成器对象crawler.fetch()
        Task(crawler.fetch())
        # 初始化Task对象以后,把fetch()给驱动到了fetch中的yied f1就完事了
        # 接下来靠事件循环
    print('-----------------')
    # 接下来,只需等待已经注册的事件发生
    # 事件循环就像心脏一般,只要它开始跳动,整个程序就会持续运行
    loop()
    print(f'程序运行时间:{time.time() - start}s')

4.5 用 yield from 改进生成器协程

yield from基本概念参考:yield from

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值