• 协程


    通常在python中设计并发编程都是使用多进程和多线程来实现的,由于GIL锁的的存在对于CPU密集型的任务通常使用多进程来实现,而对于IO密集型任务通常采用多线程来实现,让线程在调度的时候让出GIL,从而在表面上实现并发。其实对于IO密集型任务我们还可以利用协程来实现。

    协程

    协程,被称为微线程。协程是运行在单线程中的并发,通过程序的切换省去了线程之间切换的开销,提高了效率。asyncio异步模块就是协程模块。协程也不需要锁的机制因为协程是在同一个线程之中切换的,避免了同时访问数据的问题,依次执行效率要高得多。通过多进程+协程的方式来利用多核CPU以此提高性能。

    协程实现的生产者消费者模型。

    import random
    import time
    
    def producer(consumer):
        next(consumer)
        while True:
            data=random.randint(0,99)
            print("生产者生产了:",data)
            consumer.send(data)
            time.sleep(1)
    def consumer():
        while True:
            item=yield
            print("消费者消费了:",item)
    
    if __name__ == '__main__':
        c=consumer()
        p=producer(c)

    yield

    yield关键字在python中用来制造生成器,也就是说带有yield关键字的函数就是生成器函数,yield执行时会在yield处停住将yield后面的表达式返回(默认None),直到被next()方法调用才会继续执行。当没有办法yield时就会抛出异常。除了用next()语法,for循环遍历也是可以取到所有值。具体内容可参考以前的文章。生成器。

    send

    send的语法可以让yield接收外部发来的值,这样一个生成器就变成了协程。每个生成器都可以执行send()方法为yield 发送数据,此时yield不但可以暂停并返回函数,还可以接受send传过来的值并重新激活函数,将值赋给一个变量x(x = yield xxxxx)。

    def func():
        i=0
        while True:
           x=yield i
           i+=1
           print("第{}次执行fun函数,x的值时{}".format(i,x))
    g=func()
    next(g)
    g.send("hello")
    g.send("python")
    
    
    >>>>第1次执行fun函数,x的值时hello
        第2次执行fun函数,x的值时python

    协程可以处于下面四个状态中的一个。当前状态可以导入inspect模块,使用inspect.getgeneratorstate(...) 方法查看,该方法会返回下述字符串中的一个。

    • 'GEN_CREATED'  等待开始执行。

    • 'GEN_RUNNING'  协程正在执行。

    • 'GEN_SUSPENDED' 在yield表达式处暂停。

    • 'GEN_CLOSED'   执行结束。

    @asyncio.coroutine和yield from

    在python3.4开始asyncio加入python的标准库中。可以通过此来轻松的完成协程实现的一部IO操作。asyncio是一个基于事件循环的异步IO模块,通过yield from,我们可以将协程asyncio.sleep()的控制权交给事件循环,然后挂起当前协程;之后,由事件循环决定何时唤醒asyncio.sleep,接着向后执行代码。@asyncio.coroutine用于将函数声明为协程。yield from就是等待另一个函数的返回。

    import asyncio
    import datetime
    
    @asyncio.coroutine  # 声明一个协程
    def display_date(num, loop):
        while True:
            print("任务{}完成,time:{}".format(num,datetime.datetime.now()))
            yield from asyncio.sleep(2)  # 阻塞直到协程sleep(2)返回结果
    loop = asyncio.get_event_loop()  # 获取一个event_loop
    tasks = [display_date(1, loop), display_date(2, loop)]
    loop.run_until_complete(asyncio.gather(*tasks))  # "阻塞"直到所有的tasks完成
    loop.close()
    
    >>>
    任务1完成,time:2018-09-23 14:16:12.091581
    任务2完成,time:2018-09-23 14:16:12.092156
    任务1完成,time:2018-09-23 14:16:14.093619
    任务2完成,time:2018-09-23 14:16:14.094123
    任务1完成,time:2018-09-23 14:16:16.097316
    任务2完成,time:2018-09-23 14:16:16.097562
    任务1完成,time:2018-09-23 14:16:18.099375
    任务2完成,time:2018-09-23 14:16:18.100089

    async、await

    在python3.5中,引入async和await 关键字。让协程独立于生成器而存在。

    import asyncio
    import datetime
    
    # @asyncio.coroutine  # 声明一个协程
    async def display_date(num, loop):
        while True:
            print("任务{}完成,time:{}".format(num,datetime.datetime.now()))
            # yield from asyncio.sleep(2)  # 阻塞直到协程sleep(2)返回结果
            await asyncio.sleep(2)
    loop = asyncio.get_event_loop()  # 获取一个event_loop
    tasks = [display_date(1, loop), display_date(2, loop)]
    loop.run_until_complete(asyncio.gather(*tasks))  # "阻塞"直到所有的tasks完成
    loop.close()

    asyncio模块

    asyncio的使用主要分为创建事件循环、指定循环模式并运行、关闭循环。

    通常使用asyncio.get_event_loop()创建事件循环。运行循环有两种方法:一是调用run_until_complete()方法,二是调用run_forever()方法。前者内置add_done_callback 回调函数,后者则可以自定义此函数。

    import asyncio
    async def func(future):
        await asyncio.sleep(1)
        future.set_result("执行!")
    if __name__ == '__main__':
        loop=asyncio.get_event_loop()
        future=asyncio.Future()
        asyncio.ensure_future(func(future))
        print(loop.is_running())
        loop.run_until_complete(future)
        print(future.result())
        loop.close()
    import asyncio
    async def func(future):
        await asyncio.sleep(1)
        future.set_result("执行!")
    def call_result(future):
        print(future.result())
        loop.stop()
    if __name__ == '__main__':
        loop=asyncio.get_event_loop()
        loop = asyncio.get_event_loop()
        future = asyncio.Future()
        asyncio.ensure_future(func(future))
        future.add_done_callback(call_result)
        try:
            loop.run_forever()
        finally:
            loop.close()

    greenlet模块

    greenlet为第三方库,使用前先安装pip install greenlet。

    这个库为以前的python中,当然python3中提供了前面所讲的更好的方法。该模块也可以让函数变成协程。

    from greenlet import greenlet
    import random
    import time
    def producer():
        while True:
            data=random.randint(0,99)
            print("生产者生产了",data)
            c.switch(data)
            time.sleep(1)
    
    def consumer():
        while True:
            item=p.switch()
            print("消费者消费了",item)
    
    p=greenlet(producer)
    c=greenlet(consumer)
    c.switch()

     gevent

    gevent封装了libev(基于epoll)和greenlet两个库。遇到阻塞就会切换。既没有轮询的开销也没有线程的开销。

    import time
    import random
    import gevent
    from gevent.queue import Queue
    queue=Queue(2)
    def producer(queue,):
        while True:
            data=random.randint(0,99)
            print('生产者生产了:',data)
            queue.put(data)
            time.sleep(1)
    def consumer(queue):
        while True:
            data=queue.get()
            print("消费者消费了:",data)
    if __name__ == '__main__':
    
        p=gevent.spawn(producer,queue)
        c = gevent.spawn(consumer, queue)
        gevent.joinall([p,c])
  • 相关阅读:
    只有APE文件的情况下自己制作CUE文件的方法转,经过本人验证真实可靠good!20101128终极解决版
    无法加载登录用户界面DLL MyGina.dll 解决
    sql2000_简单入门,以前的笔记
    dell_又一例:raid的变化(断电后,可能没有电池造成)
    MSSQL使用帮助租赁空间后的数据库链接
    PS创建动作并利用批处理命令自动处理多图片转
    office2003修复安装,缺少SKU011.CAB文件_解决
    使用双wan口的企业/校园网解决方案初探
    我机也中病毒MS Driver Management Service(原创)
    win2003开机有问题:Windows 无法查询 {7B849a69220F451EB3FE2CB811AF94AE} 的 DllName 注册表项,它将不会被加载。最有可能的原因是注册错误。
  • 原文地址:https://www.cnblogs.com/austinjoe/p/9692875.html
Copyright © 2020-2023  润新知