美文网首页
Python协程(14)

Python协程(14)

作者: 纳米君 | 来源:发表于2018-07-29 17:44 被阅读26次

    有了进程、线程,为什么还有个协程呢?
    因为进程或线程的切换都是由操作系统决定的,存在切换开销问题,而协程的切换是由程序决定的,没有线程切换开销问题,执行性能更高些。当线程数量越多,协程优势更明显。
    另外,协程是单线程的(也就无法利用多核CPU),操作共享变量不需要加锁,所以执行效率比多线程高。

    Python的协程是通过生成器generator实现的,asyncio是Python 3.4版本引入的标准库,提供异步IO的支持。
    以前的协程定义方式是在方法上加一个装饰器@asyncio.coroutine,新语法是在方法前加一个关键字async即可。相应的,方法里的yield from也要变成await

    协程执行分以下4个步骤:

    1. 定义协程 async def get_test(url): pass
    2. 获取事件循环 loop = asyncio.get_event_loop()
    3. 获取tasks tasks = [get_test(url) for url in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
    4. 执行协程 协程并发时,loop.run_until_complete(asyncio.wait(tasks)),当只有一个协程时,loop.run_until_complete(get_test(url))

    直接上代码:

    1. 装饰器方式:
    import asyncio
    
    @asyncio.coroutine
    def get_test(url):
        conn = asyncio.open_connection(url, 80)
        reader, writer = yield from conn
        header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % url
        writer.write(header.encode('utf-8'))
        yield from writer.drain()
        text = yield from reader.read()
        print('%s text > %s' % (url, text))
        writer.close()
    
    
    loop = asyncio.get_event_loop()
    tasks = [get_test(url) for url in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
    # run_until_complete接受future,task是future的子类,接受coroutine时,会自动封装成task
    loop.run_until_complete(asyncio.wait(tasks))
    # 事件循环不再接受新的协程任务
    loop.close()
    
    1. async方式:
    import asyncio
    
    
    async def get_test(url):
        conn = asyncio.open_connection(url, 80)
        reader, writer = await conn
        header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % url
        writer.write(header.encode('utf-8'))
        await writer.drain()
        text = await reader.read()
        print('%s text > %s' % (url, text))
        writer.close()
    
    
    loop = asyncio.get_event_loop()
    tasks = [get_test(url) for url in ['www.sina.com.cn', 'www.sohu.com', 'www.163.com']]
    # run_until_complete接受future,task是future的子类,接受coroutine时,会自动封装成task
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
    1. asyncio比较麻烦,需要自己定义报头,可以安装aiohttp库。aiohttp默认最大支持1024个协程同时进行,因此下面的代码加入了并发限制:
    import asyncio
    
    import aiohttp
    
    
    async def get_test(url, semaphore):
        async with semaphore:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as res:
                    print('%s text > %s' % (url, res.status))
                    # 需要睡一会,才能体现并发限制效果,不能用time.sleep(2)
                    await asyncio.sleep(2)
                    return await res.read()
    
    
    # aiohttp默认同时最大支持1024个协程的进行
    semaphore = asyncio.Semaphore(2)
    loop = asyncio.get_event_loop()
    # 返回future对象
    tasks = [asyncio.ensure_future(get_test(url, semaphore)) for url in
             ['https://www.sina.com.cn', 'https://www.sohu.com', 'https://www.163.com', 'https://www.sohu.com',
              'https://www.163.com', 'https://www.sohu.com', 'https://www.163.com']]
    # run_until_complete接受future,task是future的子类,接受coroutine时,会自动封装成task
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    for v in tasks:
        print(v.result())
    
    

    执行时,控制台每2s输出2个结果。

    相关文章

      网友评论

          本文标题:Python协程(14)

          本文链接:https://www.haomeiwen.com/subject/yeuzmftx.html