python并发之协程

概述

协程(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.futuresasyncio 中,它们都表示带有延迟的操作。表示一个操作的执行情况,包括是否完成 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_taskasyncio.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.waitasyncio.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))

参考资料

python协程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章