概述
协程
(Coroutine) 协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。
协程有以下优势:
- 执行效率极高。因为子程序切换(函数)不是线程切换,由程序自身控制,没有切换线程的开销。
- 不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,因此执行效率高很多。
协程的使用
Python2.x
通过gevent
三方库实现协程的功能。
Python 3.4
版本引入的标准库async
,直接内置了对异步IO的支持。
Python3.5
版本引入了async/await
语法。对比Python3.4有些语法的差异。
下面介绍协程使用是基于Python 3.5
版本。
协程支持的基本单元是一个coroutine
await
后一般接一个coroutine
.await
后面跟的必须是一个Awaitable
对象,或者实现了相应协议的对象Coroutine
类继承了Awaitable
.
我们使用的asyncio.create_task
asyncio.sleep
都是一个coroutine
.
协程遇到await
会切换。
async
语法糖,标记为coroutine
类型。await
语法糖,切换操作。后面跟一个coroutine
.asyncio.create_task
创建任务, 参数要是一个coro
,返回一个task
asyncio.gather
将一组 task(序列),执行asyncio.wait
将一组coro
(list),转化为一个coro
asyncio.run
循环执行任务。参数要是一个coro
asyncio.run
相当于老版本代码。
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(coro)
finally:
loop.close()
async def crawl_page_1(url):
print("crawling {}".format(url))
sleep_time = int(url.split('_')[-1])
await asyncio.sleep(sleep_time)
print('OK {}'.format(url))
async def main_1(urls):
tasks = [asyncio.create_task(crawl_page_1(url)) for url in urls]
for task in tasks:
await task
#---------------test-----------------
if __name__ == '__main__':
#main(['url_1', 'url_2', 'url_3', 'url_4'])
asyncio.run(main_1(['url_1', 'url_2', 'url_3', 'url_4']))
Future
Python 中的 Futures
模块,位于 concurrent.futures
和 asyncio
中,它们都表示带有延迟的操作。表示一个操作的执行情况,包括是否完成 done()
,返回结果result()
,设置完成回调add_done_callback(fn)
等。
线程池executor.submit(func)
返回一个Future
.
import requests
import time
import concurrent.futures
import threading
def download_one_future(url):
resp = requests.get(url)
print('Read {} from {}'.format(len(resp.content), url))
def download_all_future(sites):
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
to_do = []
for site in sites:
future = executor.submit(download_one, site)
to_do.append(future)
#等任务结束
for future in concurrent.futures.as_completed(to_do):
print('result=', future.result())
if __name__ == '__main__':
start_time = time.perf_counter()
download_all_future(sites)
end_time = time.perf_counter()
print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
asyncio.create_task
和asyncio.gather
的用法
async def download_one_async(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
print('Read {} from {}'.format(resp.content_length, url))
async def download_all_async(sites):
tasks = [asyncio.create_task(download_one(site)) for site in sites]
await asyncio.gather(*tasks)
sites = [
'https://baike.baidu.com/item/%E5%8C%BA%E5%9D%97%E9%93%BE/13465666?fr=aladdin',
'https://baike.baidu.com/item/%E6%AF%94%E7%89%B9%E5%B8%81%E6%8C%96%E7%9F%BF%E6%9C%BA/12536531?fr=aladdin',
'https://baike.baidu.com/item/%E5%BA%9E%E6%B0%8F%E9%AA%97%E5%B1%80',
'https://baike.baidu.com/item/%E6%8A%97%E7%94%9F%E7%B4%A0'
]
download_all_async(sites)
print('Download {} sites in {} seconds'.format(len(sites), end_time - start_time))
asyncio.wait
和asyncio.run
的用法
async def test(i):
print("test_1",i)
await asyncio.sleep(1)
print("test_2",i)
tasks=[test(i) for i in range(5)]
asyncio.run(asyncio.wait(tasks))