zoukankan      html  css  js  c++  java
  • python协程

    不知道你有没有被问到过有没有使用过的python协程?

    协程是什么?

    协程是一种用户态轻量级,是实现并发编程的一种方式。说到并发,就能想到了多线程 / 多进程模型,是解决并发问题的经典模型之一。

    但是随刻客户端数量达到一定量级,进程上下文切换占用了大量的资源,线程也顶不住如此巨大的压力,对于多线程应用,CPU通过切片的方式来切换线程间的执行,

    线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。线程是抢占式的调度,而协程是协同式的调度,也就是说,协程需要自己做调度。

    协程有什么用?

    在别的语言中协程意义不大,多线程可解决问题,但是python因为他有GIL(Global Interpreter Lock 全局解释器锁 )在同一时间只有一个线程在工作,如果一个线程里面I/O操作特别多,协程就比较适用;

    在python中多线程的执行情况如下图:

    怎么实现协程?

    python2.7中用生成器实现协程

    在python3.7以后使用asyncio 和 async / await 的方法实现协程,实现过程就变得很简单

    具体说明:简单的代码示例

    
    
    import asyncio
    import time


    async def sub_function(str):
    print(' {}'.format(str))
    sleep_time = int(str.split('_')[-1])
    await asyncio.sleep(sleep_time)
    print('OK {}'.format(str))


    async def main(strs):
    for str in strs:
    await sub_function(str)

    # asyncio.run(main()) 作为主程序的入口函数,在程序运行周期内,只调用一次 asyncio.run
    t0 = time.time()
    asyncio.run(main(['str_1', 'str_2', 'str_3', 'str_4']))
    t1 = time.time()
    print("Total time running: %s seconds" %(str(t1 - t0)))

    输出结果:一共是 10s 和我们顺序分析 分别等待 1 2 3 4 秒 好像没有什么提升作用,继续往下看

    执行协程的方法有三种:

    1. 入上例所示,用await 来调用实现,但是await 执行的效果,和 Python 正常执行是一样的,也就是说程序会阻塞在这里,进入被调用的协程函数,执行完毕返回后再继续,而这也是 await 的字面意思。

      await 是同步调用,相当于我们用异步接口写了个同步代码,所以运行时间没有得到提升。

    2. 上栗的异步想过没有体现出来,接下来我们使用另一个概念  task

    async def sub_function(str):
        print(' {}'.format(str))
        sleep_time = int(str.split('_')[-1])
        await asyncio.sleep(sleep_time)
        print('OK {}'.format(str))
    
    
    async def main(strs):
       # tasks
    = [asyncio.create_task(sub_function(str)) for str in strs] for task in tasks: await task # asyncio.run(main()) 作为主程序的入口函数,在程序运行周期内,只调用一次 asyncio.run t0 = time.time() asyncio.run(main(['str_1', 'str_2', 'str_3', 'str_4'])) t1 = time.time() print("Total time running: %s seconds" %(str(t1 - t0)))

    们有了协程对象后,便可以通过 asyncio.create_task 来创建任务。任务创建后很快就会被调度执行,

    这样,我们的代码也不会阻塞在任务这里。用for task in tasks: await task 即可。这次,你就看到效果了吧,结果显示,运行总时长等于运行时间最长一句。

    运行结果:

     3. 上面的task也可以用下面的方式写:

    async def main(strs):
        tasks = [asyncio.create_task(sub_function(str)) for str in strs]
        await asyncio.gather(*tasks)

    唯一要注意的是,*tasks 解包列表,将列表变成了函数的参数;与之对应的是, ** dict 将字典变成了函数的参数。

    在实际中,我们会遇到接口超时,我们就需要取消的情况,这种情况该怎么处理呢?再进一步,如果某些协程运行时出现错误,又该怎么处理呢?

    import time
    import asyncio
    
    async def worker_1():
        await asyncio.sleep(1)
        return 1
    
    async def worker_2():
        await asyncio.sleep(2)
        return 2 / 0
    
    async def worker_3():
        await asyncio.sleep(3)
        return 3
    
    async def main():
        task_1 = asyncio.create_task(worker_1())
        task_2 = asyncio.create_task(worker_2())
        task_3 = asyncio.create_task(worker_3())
    
        await asyncio.sleep(2)
        task_3.cancel()
    
        res = await asyncio.gather(task_1, task_2, task_3, return_exceptions=True)
        print(res)
    
    t0 = time.time()
    asyncio.run(main())
    t1 = time.time()
    print("Total time running: %s seconds" %(str(t1 - t0)))

    要注意return_exceptions=True这行代码。这个参数默认值为False, 如果不设置这个参数,错误就会完整地 throw 到我们这个执行层,从而需要 try except 来捕捉,

    这也就意味着其他还没被执行的任务会被全部取消掉。为了避免这个局面,我们将 return_exceptions 设置为 True 即可。

     线程能实现的,协程都能做到.

  • 相关阅读:
    java.util报错
    mysql的sql_mode合理设置
    MySQL查询本周、上周、本月、上个月份数据的sql代码
    连接池配置
    js实现内容点击复制
    myeclipse 打开jsp文件出错
    Spring可二次开发常用接口、类及其源码详解
    Redis学习之Redis集群模式缺陷及其处理
    Redis学习之API学习及Jedis源码原理分析
    Redis学习之4种模式实践及机制解析(单机、主从、哨兵、集群)
  • 原文地址:https://www.cnblogs.com/qiutian-guniang/p/12822824.html
Copyright © 2011-2022 走看看