Python协程(14)

2018-07-29  本文已影响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个结果。

上一篇 下一篇

猜你喜欢

热点阅读