并发和并行
并发
- 同一时刻只允许一个操作,线程/任务之间相互切换,达到并发处理任务
- 单线程:协程(asyncio)
- 多线程:Futures(threading)
- 并发通常应用与I/O操作频繁的场景
并行
- 同一时刻同时发送
- 多进程
- 并行通常应用于CPU heavy的场景
单线程并发-协程
单个协程实现并发,建议使用I/O密集型场景
python3.7及以上
描述 | 备注 | |
---|---|---|
async | 异步函数申明 | 将普通函数申明为异步函数 |
await | 异步函数调用 | 同步调用异步函数,返回协程对象 |
asyncio.create_taks() | 创建任务 | |
asynic.gater(*tasks) | 执行task | |
asynic.run | 触发运行 |
协程实例
协程使用步骤:
- 使用asynic修饰函数,定义为异步函数
- await调用异步函数,注意:同步调用,需要task来实现真正的并发
- task创建:task任务
asynic.create_task(异步函数)
- task调度:创建调度器,统一调度任务
await asynic.gather(*task)
- 运行协程:
asynic.run(异步函数)
- 第三方库要支持协程,如
request
不支持协程,改用aiohttp
import asyncio
async def crawl_page(url):
print('crawling {}'.format(url))
sleep_time = int(url.split('_')[-1])
await asyncio.sleep(sleep_time)
print('OK {}'.format(url))
async def main(urls):
tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
await asyncio.gather(*tasks)
asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
多线程并发- future
- 全局解释器:Python 便引入了全局解释器锁,也就是同一时刻,只允许一个线程执行。当然,在执行 I/O 操作时,如果一个线程被 block 了,全局解释器锁便会被释放,从而让另一个线程能够继续执行
描述 | 备注 | |
---|---|---|
future | future对象 | 将处于等待状态的操作包裹起来放到队列 |
ThreadPoolExcutor | 创建线程池 | max_workers=5 |
PorcessPoolExcutor | 创建进程池 | max_workers=5 |
executor.submit(func) | func被包裹成future对象,返回future实例 | |
future.as_completed(fn) | future完成后,回调函数fn | |
future.result() | future完成后,返回对应结果或异常 |
实例
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
import requests
import time
def download_one(url):
resp = requests.get(url)
print('Read {} from {}'.format(len(resp.content), url))
def download_all(urls):
with ThreadPoolExecutor(5) as excutor:
to_do = []
for url in urls:
future = excutor.submit(download_one, url)
to_do.append(future)
for future in as_completed(to_do):
future.result()
if __name__ == '__main__':
sites = [
"https://www.19lou.com",
"https://food.19lou.com",
"https://tour.19lou.com",
"https://auto.19lou.com",
"https://fashion.19lou.com",
"https://sweet.19lou.com",
"https://love.19lou.com",
"https://marry.19lou.com",
"https://money.19lou.com"
]
st = time.perf_counter()
download_all(sites)
et = time.perf_counter()
网友评论