zoukankan      html  css  js  c++  java
  • python协程 --- asyncio库

    从线程理解协程

    一个cpu想要同时实现多任务的执行,需要操作系统调度cpu去执行多个线程,每个线程执行不同的单个任务,从而实现多任务的执行。线程是操作系统的资源之一,创建或者销毁线程都由操作系统执行,每个线程都由自己独立的资源,例如临时变量的数据,函数调用的堆栈信息,或者当前线程执行的当前位置,发生线程切换时候,线程会保存这些资源,记录当前执行的状态,当cpu在次对该线程发起调度时,恢复上一次的离开时的状态执行。这些数据以及状态信息,通常称为线程上下文。

     

    每个线程都有自己的线程上下文,而线程之间的切换必将引起上下文的切换,cpu执行一个线程时,这部分数据可以直接从寄存器或者多级缓存中读取,读取速度是可观的。线程的切换意味着另一个线程的被唤醒,新线程的数据需要被加载,就可能会覆盖掉部分的上个线程在寄存器或者缓存的数据,下次原线程恢复时需要重新从内存中读取数据到寄存器。多个cpu的情况下,这种方式情况更为严重,因为一个线程可以被随机的被某个cpu执行,上下文切换更为频繁,通常我们会考虑将一个线程绑定到某个cpu上,使其只被这个cpu调度,从而提高效率。

    协程

    线程的调度更加的耗费系统资源。而使用协程就可以避免这些问题。单个线程的执行是顺序执行的,如果需要完成3个任务(每个任务以一个函数表示),这三个任务将会一个个按照顺序执行,如果每个任务中包含部分IO操作,线程也只能同步阻塞等待IO返回,无法执行其他的任务。而协程实现了在这一个线程中同时调度执行这多个任务,避免这些耗时IO操作的同步阻塞等待,提高了任务的执行效率。

    asyncio基本使用

    官方文档:https://docs.python.org/zh-cn/3/library/asyncio-task.html#running-an-asyncio-program

    定义任务

    定义任务使用了async关键字,然后和普通函数的定义相同

    import asyncio
    
    async task1():
        print("task1 ++++")
        return "abc"
    
    if __name__ = "__main__":
        asyncio.run(task1())    # run方法在3.7版本才实现

    定义了任务task1,然后使用 asyncio.run(task1())执行了该任务。

    在run函数执行任务时,实际上及创建了一个事件循环,这个事件循环会反复监听这个任务的状态,在多个任务时候,还会自动实现多个任务的调度。

    事件循环

    上面使用run函数运行时,会创建一个事件循环,事件循环中可以添加多个任务,并负责调度这些任务,在3.7之前,就需要手动的创建事件循环,添加任务才能保证事件的运行。

    import asyncio
    
    async task1():
        print("task1 ++++")
        return "abc"
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()   # 获得一个事件循环
        loop.run_until_complete(task1())   # 运行事件循环,直到task1这个任务结束,事件循环终止

    这里只添加了一个task1任务,如果还存在其他的如task2任务需要一并加入,需要使用gather方法

    async task1():pass
    async task2():pass
    async task3():pass
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()   # 获得一个事件循环
        tasks = asyncio.gather(task1(), task2(), task3())
        loop.run_until_complete(tasks)

    运行任务

    任务的运行需要交给事件循环,事件循环可以同时调度多个任务,前提是这些任务存在IO操作。因为协程任务始终是在一个线程上进行调度的,如果一个任务没有IO(例如无限的死循环),始终占据这个线程,其他的任务将无法被调度执行。

    任务可以由事件循环统一调度,这些任务随机的被执行(聚集模式)也可以在一个任务中去启动一个子任务,然后等待子任务执行完毕在继续执行原任务(串行执行模式),等待任务的运行需要使用awati关键字,实现该任务同步阻塞等待子任务的效果。

    import asyncio
    
    async task1():
        print("task1 ++++")
        return "abc"
    
    async task2():
        print("task2 ++++")
        await task1()
        return 123
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()   # 获得一个事件循环
        loop.run_until_complete(task2())   # 运行事件循环,直到task1这个任务结束,事件循环终止

    loop事件循环启动task2,task2中调用并同步等待task完成。也可以在task2中将task1添加到事件循环中,两个任务协同调用执行。

    import asyncio
    
    async task1():
        print("task1 ++++")
        return "abc"
    
    async task2():
        t = asyncio.create_task(task1()) # 在loop中添加一个任务,返回任务对象,该任务将会自动被调用执行
        # await t              # 也可以等待该t 任务执行完毕。 
        return 123
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()   # 获得一个事件循环
        loop.run_until_complete(task2())   # 运行事件循环,直到task1这个任务结束,事件循环终

    asyncio.sleep()

    该方法也是一个协程对象,该协程对象的功能是等待一定的时常,通常我们在自己的任务中以await的方式调用 asyncio.sleep()方法,使得我们的协程任务等待一定时长再执行,等待时线程可以去执行其他的任务。

    等待对象

    三种等待对象:

      协程对象:async def 方式定义的协程对象

      task对象:使用asyncio.create_task方式返回的对象

      future对象:

    执行回调

    task任务可以被注册一个回调函数,调用时会自动注入该task对象作为参数,所以回调函数只能是一参函数。如果回调函数需要有其他参数,需要提前绑定一部分参数值,使成为一参函数作为回调函数(可以使用装饰器,或者functool中的partail方法绑定实现参数绑定)。

    import asyncio
    import functools
    
    def callback(task, args):
        print(task, args)
        task.result
        return 123
    
    async task1():
        print("task1 ++++")
        return "abc"
    
    async task2():
        t = asyncio.create_task(task1()) # 在loop中添加一个任务,返回任务对象,该任务将会自动被调用执行
        t.add_callback_done(
                functools.partail(callback, args="abc")   # callback函数绑定了一个args="abc"
                )
        asyncio.sleep(1)
        return 123

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    leetcode701. Insert into a Binary Search Tree
    leetcode 958. Check Completeness of a Binary Tree 判断是否是完全二叉树 、222. Count Complete Tree Nodes
    leetcode 110. Balanced Binary Tree
    leetcode 104. Maximum Depth of Binary Tree 111. Minimum Depth of Binary Tree
    二叉树
    leetcode 124. Binary Tree Maximum Path Sum 、543. Diameter of Binary Tree(直径)
    5. Longest Palindromic Substring
    128. Longest Consecutive Sequence
    Mac OS下Android Studio的Java not found问题,androidfound
    安卓 AsyncHttpClient
  • 原文地址:https://www.cnblogs.com/k5210202/p/13070041.html
Copyright © 2011-2022 走看看