zoukankan      html  css  js  c++  java
  • 异步编程

    Python异步编程

    前言

    现在是 Python3.5 以后已经进入异步时代

    Python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病。然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了Python性能方面的短板.

    • python3.0时代,标准库里的异步网络模块:select(非常底层)

    • python3.0时代,第三方异步网络库:Tornado

    • python3.4时代,asyncio:支持TCP,子进程.直接内置了对异步IO的支持。

    现有的python 异步框架

    • tornado、fasapi 、djanao-3.0 asgi、aiohttp 等等等。。。

    python 中的主流django框架都在 往异步靠拢 不得不说 要进入异步的时代了 ,而我还不会异步编程那就可悲了。

    核心重点 异步编程 来提高性能。

    主要以 这以下三点来聊:

    • 协程
    • asyncio模块进行异步编程
    • 实战案例

    协程

    协程不是计算机提供的,计算机提供了进程和线程的概念,而协程是我们程序员人为。也叫微线程,用户态上下文切换的一种技术

    通过一个线程去代码之间游走切换之间去运行。

    例如:
    普通的执行

    def func1():
        print(1)
        
        print(2)
    
    def func(2):
        print(3)
        
        print(4)
        
    func1()
    func2()
    

    结果:

    1
    2
    3
    4
    

    实现协程

    • greenlet ,早期模块
    • yield 关键字
    • asynico 装饰器(py.34)
    • asny、await 关键字(py3.5)[官方推荐]

    greenlet实现协程

    下载

    pip3 install greenlet
    
    from greenlet import greenlet
    
    def func1(): 
        print(1)            # 第2步 输出1
        gr2.switch()        # 第3步 切换到 func2 函数执行
        print(2)            # 第6步 输出2
        gr2.switch()        # 第7步 切换到 func2 函数执行
    
    def func2():
        print(3)            # 第4步 输出3 
        gr1.switch()        # 第5步 切换到 func1 函数执行
        print(4)            # 第8步 输出4
    
    
    gr1 = greenlet(func1)
    gr2 = greenlet(func2)
    
    gr1.switch() # 第1步:去执行func1 函数
    

    结果:

    1
    3
    2
    4
    

    yield 关键字

    def func1():
    
        yield 1
        yield from func2()
        yield 2
    
    
    
    def func2():
        yield 3
        yield 4
    
    
    f1 = func1()
    for item in f1:
        print(item)
    

    结果

    1
    3
    2
    4
    

    yeild 这个实现的 了解即可

    重头戏 asyncio

    必须在python3.4及之后的版本才能用

    import asyncio
    
    @asyncio.coroutine
    def func1():
        print(1)
        # 现在我用得失 sleep(2) 我要是换成 网络请求 意义就比重大了 
        yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
        print(2)
    
    @asyncio.coroutine
    def func2():
        print(3)
        yield from asyncio.sleep(2)
        print(4)
    
    
    tasks = [
        asyncio.ensure_future(func1()),
        asyncio.ensure_future(func2())
    
    ]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    
    # 本来 应该等待 4 秒 执行时间其实也就是1秒 没有等待4秒 
    

    注意⚠️:遇到io阻塞自动切换
    结果

    1
    3
    2
    4
    

    async & await 关键字

    在Python3.5 及以后的版本 才能用

    import asyncio
    
    async def func1():
        print(1)
        await asyncio.sleep(2)
        print(2)
    
    
    async def func2():
        print(3)
        await asyncio.sleep(2)
        print(4)
    
    tasks = [
        asyncio.ensure_future(func1()),
        asyncio.ensure_future(func2())
    
    ]
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait(tasks))
    

    那我们最常用的就是 async & await 关键字

    协程的意义

    在一个线程中如果遇到io等待的时间,线程不会一直等待,利用空闲的时间去执行其他的任务 如果那个任务有结果了 在回去拿结果。

    案例:
    网络io请求获取三张图片

    普通方式(同步编程)

    pip3 install requests
    
    import requests
    
    def download_image(url):
        print(f'开始下载图片:{url}')
        response = requests.get(url)
        print(f'下载完成')
        file_name = url.rsplit("/")[-1]
        with open(file_name,'wb',) as f:
            f.write(response.content)
    
    
    
    if __name__ == '__main__':
        url_list = [
            'http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg',
            'http://a2.att.hudong.com/36/48/19300001357258133412489354717.jpg',
            'http://a0.att.hudong.com/64/76/20300001349415131407760417677.jpg',
        ]
        for url in url_list:
            download_image(url)
    

    结果:

    开始下载图片:http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg
    下载完成
    开始下载图片:http://a2.att.hudong.com/36/48/19300001357258133412489354717.jpg
    下载完成
    开始下载图片:http://a0.att.hudong.com/64/76/20300001349415131407760417677.jpg
    下载完成
    

    看结果 是安顺序执行的 如果没哥请求是两分钟的话 那就需要足足等待6分钟。
    如果我们用 异步做就会大大的提高性能。

    协程方式(异步编程)

    import asyncio
    import aiohttp
    
    async def fetch(session,url):
        print(f'发送请求{url}')
        async with session.get(url,verify_ssl=False) as response:
    
            content =  await response.read()
            file_name = url.rsplit('/')[-1]
            with open(file_name,'wb') as f:
                f.write(content)
        print('下载完成')
    
    async def main():
        async with aiohttp.ClientSession() as session:
            url_list = [
                'http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg',
                'http://a2.att.hudong.com/36/48/19300001357258133412489354717.jpg',
                'http://a0.att.hudong.com/64/76/20300001349415131407760417677.jpg',
            ]
            tasks = [
                asyncio.create_task(fetch(session,url)) for url in url_list
            ]
            await asyncio.wait(tasks)
    
    if __name__ == '__main__':
        asyncio.run(main())
    

    结果:

    发送请求http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg
    发送请求http://a2.att.hudong.com/36/48/19300001357258133412489354717.jpg
    发送请求http://a0.att.hudong.com/64/76/20300001349415131407760417677.jpg
    下载完成
    下载完成
    下载完成
    
  • 相关阅读:
    C#事件和委托的区别
    已知有个rand7()的函数,返回1到7随机自然数,让利用这个rand7()构造rand10()随机1~10
    如何搭建github+hexo博客-转
    ngRouter和ui-router区别
    JS数组追加数组采用push.apply的坑(转)
    vue中关于computed的一点理解
    simplify the life ECMAScript 5(ES5)中bind方法简介
    github使用-知乎的某小姐的一篇文章
    Jade 模板引擎使用
    玩转Nodejs日志管理log4js(转)
  • 原文地址:https://www.cnblogs.com/jiangchunsheng/p/12973346.html
Copyright © 2011-2022 走看看