zoukankan      html  css  js  c++  java
  • [Python 多线程] asyncio (十六)

    asyncio

    该模块是3.4版本加入的新功能。

    先来看一个例子:

    def a():
        for x in range(3):
            print('a.x', x)
    
    def b():
        for x in 'abc':
            print('b.x', x)
    
    a()
    b()
    
    #运行结果:
    a.x 0
    a.x 1
    a.x 2
    b.x a
    b.x b
    b.x c
    

      这个例子是一个典型的串行程序,两个函数调用是在主线程中顺序执行。

    有以下几种方法可以让这段程序改为并行:

    1. 生成器

    2. 多线程

    3. 多进程

    4. 协程

    1)生成器方法:

    def a():
        for x in range(3):
            yield x
    
    
    def b():
        for x in 'abc':
            yield x
    
    
    m = a()
    n = b()
    
    for _ in range(3):
        print(next(m))
        print(next(n))
    
    #运行结果:
    0
    a
    1
    b
    2
    c
    

      使用生成器来实现交替执行。这两个函数都有机会执行,这样的调度不是操作系统的进程、线程完成的,而是用户自己设计的。

    2)多线程方法:

    import threading,time
    
    def a():
        for x in range(3):
            time.sleep(0.0001)
            print('a.x',x)
    
    def b():
        for x in 'abc':
            time.sleep(0.0001)
            print('b.x',x)
    
    threading.Thread(target=a).start()
    threading.Thread(target=b).start()
    
    #运行结果:
    a.x 0
    b.x a
    a.x 1
    b.x b
    a.x 2
    b.x c
    

      主要使用sleep函数强制切换来实现伪并行。

    3)多进程方式:

    import multiprocessing
    
    def a():
        for x in range(3):
            print('a.x',x)
    
    def b():
        for x in 'abc':
            print('b.x',x)
    
    if __name__ == '__main__':
        multiprocessing.Process(target=a).start()
        multiprocessing.Process(target=b).start()
    
    #运行结果:
    a.x 0
    a.x 1
    a.x 2
    b.x a
    b.x b
    b.x c
    

      多进程方式才是真正的并行。

    4)协程方法:

    协程,需要使用到 asyncio 标准库,是Python3.4版本加入的新功能,底层基于selectors实现,包括异步IO、事件循环、协程等内容。

    事件循环:

    事件循环是asyncio提供的核心运行机制。

    程序开启一个无限的循环,使用者会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。

    4.1 事件循环基类

    asyncio.BaseEventLoop  这个类是一个实现细节,它是asyncio.AbstractEventLoop的子类,不可以直接使用
    asyncio.AbstractEventLoop  事件循环的抽象基类,这个类是是线程不安全的

    4.2 运行事件循环

    asyncio.get_event_loop()  返回一个事件循环对象,是asyncio.BaseEventLoop的实例
    asyncio.AbstractEventLoop.stop()  停止运行事件循环
    asyncio.AbstractEventLoop.run_forever()  一直运行,直到调用stop()
    asyncio.AbstractEventLoop.run_until_complete(future)  运行直到future对象运行完成,返回结果
    asyncio.AbstractEventLoop.close()  关闭事件循环
    asyncio.AbstractEventLoop.is_running()  返回事件循环的运行状态
    asyncio.AbstractEventLoop.is_closed()  如果事件循环已关闭,返回True

    4.3 协程

    协程不是进程、也不是线程,它是用户空间调度完成并发处理的方式。(同一线程内交替执行其实也是伪并发)

    并发指的是在一段时间内做了多少、并行指的是同一时刻有多少同时执行。

    进程、线程由操作系统完成调度,而协程是线程内完成调度。它不需要更多的线程,也就没有多线程切换带来的开销。

    协程是非抢占式调度,只有一个协程主动让出控制权,另一个协程才会被调度。

    协程也不需要锁机制,因为是在同一线程中执行。

    多CPU下,可以使用多线程和协程配合,既能进程并发又能发挥协程在单线程中的优势。

    Python中协程是基于生成器的。

    4.4 协程的使用

    4.4.1 Python3.4中使用@asyncio.coroutine 、 yield from

    #asyncio Python3.4
    
    import asyncio
    
    @asyncio.coroutine
    def foo(x): #生成器函数上面加了协程装饰器之后就转化成协程函数
    	for i in range(3):
    		print('foo {}'.format(i))
    		yield from asyncio.sleep(x) #调用另一个生成器对象
    
    loop = asyncio.get_event_loop() #获得一个时间循环
    loop.run_until_complete(foo(1)) #传入一个生成器对象的调用
    loop.close()
    
    
    #运行结果:
    foo 0
    foo 1
    foo 2
    [Finished in 3.3s]
    

      此例子在一个生成器函数加了协程装饰器之后,该生成器函数就转化成了协程函数。

    4.4.2 Python3.5中使用关键字 async def 、 await ,在语法上原生支持协程

    #asyncio Python3.5
    
    import asyncio
    
    async def foo(x):  #异步定义,协程定义
    	for i in range(3):
    		print('foo {}'.format(i))
    		await asyncio.sleep(x) #不可以出现yield,使用await替换
    
    print(asyncio.iscoroutinefunction(foo))
    loop = asyncio.get_event_loop()
    loop.run_until_complete(foo(1)) #传入一个协程对象的调用
    loop.close()
    
    #运行结果:
    True
    foo 0
    foo 1
    foo 2
    [Finished in 3.3s]
    

      async def 用来定义协程函数,iscoroutinefunction(func)判断func函数是否是一个协程函数。协程函数中可以不包含await、async关键字,但不能使用yield关键字。

    其它语法:async with,支持上下文的协程

    4.4.3 coroutine asyncio.wait(futures, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

    等待futures序列中的协程对象执行完成,futures序列不可以为空。

    timeout可以用于控制返回前等待的最大秒数,秒数可以是int或浮点数,如果未指定timeout,则无限制。

    #wait多个协程对象
    import asyncio
    
    @asyncio.coroutine
    def a():
    	for i in range(3):
    		print('a.x',i)
    		yield
    
    @asyncio.coroutine
    def b():
    	for i in range(3):
    		print('b.x',i)
    		yield
    
    
    loop = asyncio.get_event_loop()
    tasks = [a(),b()]
    loop.run_until_complete(asyncio.wait(tasks)) #传入一个协程对象序列
    
    loop.close()
    
    
    #运行结果:
    b.x 0
    a.x 0
    b.x 1
    a.x 1
    b.x 2
    a.x 2
    [Finished in 0.3s]
    

      

    总结:

    传统的多线程、多进程都是系统完成调度,而协程是在进程中的线程内由用户空间调度完成并发处理,主要依靠生成器来实现交替调度。

    Python3.4中使用@asyncio.coroutine、yield from调用另一个生成器对象

    Python3.5中使用关键字 async def 和 await,且不可以出现yield关键字。

  • 相关阅读:
    软件对标分析
    第一阶段绩效评估
    自律小帮手:Alpha版使用说明
    团队 电梯演讲 原型展示
    意见评论
    Alpha版(内部测试版)发布
    意见汇总
    产品对比
    团队项目-第二阶段冲刺-3
    团队项目-第二阶段冲刺-2
  • 原文地址:https://www.cnblogs.com/i-honey/p/8110824.html
Copyright © 2011-2022 走看看