1. 多线程、多进程、协程
- 计算机中提供了:线程、进程 用于实现并发(真实存在)
- 协程(Coroutine),是程序员通过代码搞出来的东西(非真实存在)
- 协程也可以被称为微线程,是一种用户态内上下文切换技,其实就是通过一个线程实现代码块相互切换执行
2. Python中有多种方式可以实现协程
- greenlet
from greenlet import greenlet def func1(): print(1) gr2.switch() # 切换到func2函数 print(2) gr2.switch() # 切换到func2函数,从上一次执行的位置继续向后执行 def func2(): print(3) gr1.switch() # 切换 print(4) gr1 = greenlet(func1) gr2 = greenlet(func2) gr1.switch() # 去执行func1函数
- yield
def func1(): yield 1 yield from func2() yield 2 def func2(): yield 3 yield 4 f1 = func1() for item in f1: print(item)
- 以上两种方法都实现了协程,但无实用意义,来回切换,反倒使程序执行速度更慢
- 如果使用协程?不要让用户手动切换,而是遇到IO操作时能自动切换
- Python3.4之后推出了asyncio模块 + Python3.5推出async、async语法,内部基于协程,并且遇到IO请求自动化切换
import asyncio async def func1(): print(1) await asyncio.sleep(2) print(2) async def func2(): print(3) await asyncio.sleep(2) print(4) tasks = [ asyncio.ensure_future(func1()), asyncio.ensure_future(func2()) ] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks))
import aiohttp import asyncio async def fetch(seesion, url): print('发送请求:', url) async with session.get(url, verify_ssl=False) as response: content = await response.content.read() file_name = url.rsplit('_')[-1] with open(file_name, mode='wb') as file_object: file_object.write(content) async def main(): async with aiohttp.ClientSession() as session: url_list = [ 'http://www.autoimg.com/....', 'http://www.autoimg.com/....', 'http://www.autoimg.com/....' ] tasks = [asyncio.create_task(fetch(session, url)) for url in url_list] await asyncio.wait(tasks) if __name__ = '__main__': asyncio.run(main))
- 在处理IO请求时,协程通过一个线程就可以实现并发操作
3. 协程、线程、进程的区别
- 线程,是计算机中可以被CPU调度的最小单元
- 进程,是计算机资源分配的最小单元(进程为线程提供资源)
- 一个进程中可以有多个线程,同一个进程中的线程可以共享此进程中的资源
- 由于CPython解释器中GIL的存在:
- 线程,适用于IO密集型操作
- 进程,适用于计算密集型操作
- 协程,也可以被称为微线程,是一种用户态内的上下文切换技术,在开发中结合遇到IO自动切换,就可以通过一个线程实现并发操作
- 故,在处理IO操作时,协程比线程更加节省开销(协程的开发难度大一些)
- 支持协程的Python框架:FastAPI、Tornado、Sanic、Django 3、aiohttp等