|
1 | 1 | 协程 Coroutine |
2 | 2 | 协程或又叫微线程, 纤程, 协程本质是在一个线程内, 通过轮询的方式执行不同的任务. |
| 3 | + 对比于进程与线程是系统CPU的能力, 协程是由程序员通过代码实现的, 其建立在系统的线程之上. |
3 | 4 |
|
4 | 5 | 工作原理 |
5 | | - 对比于进程与线程是系统CPU的能力, 协程是由程序员通过代码实现的, 其建立在系统的线程之上. |
6 | 6 | 通过应用层记录程序的上下文栈区, 实现程序运行中的跳跃. 进而实现选择代码段执行. |
7 | 7 |
|
8 | | -一般一个线程栈大小为1MB, 使用多线程, 在高并发下, cpu大部分的时间都将用于切换线程上下文. |
9 | | -由于线程的切换是在内核完成的, 会耗费额外的空间和时间. 而且由于内存都分配给线程栈了, 将频繁地进行内存置换算法, 浪费了很多cpu时间片. |
10 | | - |
11 | | -协程, 可以理解为一种在线程里跑的子线程, 它的默认栈空间很小 (比如go的协程栈默认大小为2KB). |
12 | | -当多个协程在一个线程上运行时, 协程间会切换着运行, 协程的切换完全在用户态完成, 而且时机由程序员来自行调度, 从而使得线程的并发量大大提升. |
| 8 | +协程的资源消耗 |
| 9 | + 一般一个线程栈大小为1MB, 使用多线程, 在高并发下, cpu大部分的时间都将用于切换线程上下文. |
| 10 | + 由于线程的切换是在内核完成的, 会耗费额外的空间和时间. 而且由于内存都分配给线程栈了, 将频繁地进行内存置换算法, 浪费了很多cpu时间片. |
13 | 11 |
|
14 | | -但要注意的是协程只适用于IO密集型程序(大部分时间在等待的), 对于计算密集型程序, 协程的优势并不大, 因为没有给它切换的时间, cpu大部分时间都在工作. |
| 12 | + 对比协程, 可以理解为一种在线程里跑的子线程, 它的默认栈空间很小 (比如go的协程栈默认大小为2KB). |
| 13 | + 当多个协程在一个线程上运行时, 协程间会切换着运行, 协程的切换完全在用户态完成, 而且时机由程序员来自行调度, 从而使得线程的并发量大大提升. |
15 | 14 |
|
16 | | -常见的IO密集型程序有 网络请求、文件读写、数据库查询等, |
| 15 | + 但要注意的是协程只适用于IO密集型程序(大部分时间在等待的). |
| 16 | + 对于计算密集型程序, 协程的优势并不大, 因为没有给它切换的时间, cpu大部分时间都在工作. |
| 17 | + 常见的IO密集型程序有 网络请求、文件读写、数据库查询等, |
17 | 18 |
|
18 | 19 | 优点: |
19 | 20 | 无需多线程切换的开销 |
@@ -131,15 +132,42 @@ asyncio 标准库 |
131 | 132 | async 关键字: async定义一个协程. |
132 | 133 | 格式: async def 函数名(参数): pass |
133 | 134 |
|
134 | | - await 关键字: 遇到阻塞后,先挂起当前协程(任务),让事件循环去执行其他任务(如果有的话),等待“可等待对象”执行完成后,再继续执行下面的代码。 |
135 | | - 格式: await 可等待对象 |
136 | | - 获取可等待对象的返回值 |
137 | | - 格式: 变量名 = await 可等待对象 |
| 135 | + await 关键字: 获取可等待对象的返回值, 若协程函数有返回值, 只有拿到返回值才会解除阻塞 |
| 136 | + 遇到阻塞后,先挂起当前协程(任务),让事件循环去执行其他任务(如果有的话),等待“可等待对象”执行完成后,再继续执行下面的代码。 |
| 137 | + 格式: await 可等待对象 |
| 138 | + 格式: 变量名 = await 可等待对象 |
| 139 | + 注意 |
| 140 | + await是用来等待任务返回结果的, 不是用来进行异步任务并发的 |
138 | 141 |
|
139 | 142 | 可等待对象 |
140 | 143 | 可等待对象是指可以在await语句中使用的对象,它主要有三种:协程对象、Task对象和 Future对象 |
141 | 144 |
|
142 | | - 2) 事件循环 |
| 145 | + 2) 协程对象 |
| 146 | + coroutine对象 |
| 147 | + |
| 148 | + 以async def 函数名(): pass 的格式定义的函数, 为协程函数, 当执行一个协程函数, 会返回一个协程对象. |
| 149 | + |
| 150 | + 3) Task对象 |
| 151 | + Task对象用于向事件循环中加入任务. |
| 152 | + |
| 153 | + Task用于开发调度协程, 通过asyncio.create_task(协程对象)创建(该函数于Python3.7中添加). |
| 154 | + 也可以使用asyncio.ensure_future(协程对象)创建一个Task对象. |
| 155 | + |
| 156 | + asyncio.run_until_complete的参数是一个future对象, 当传入一个协程, 其内部会自动封装成Task. |
| 157 | + |
| 158 | + 在协程嵌套中 |
| 159 | + ret1 = await fun1() |
| 160 | + ret2 = await fun2() |
| 161 | + await仅会将fun1提交到事件循环中, fun2的await并未执行 |
| 162 | + |
| 163 | + 通过将协程对象变为Task对象, 使await不阻塞 |
| 164 | + task1 = asyncio.create_task(fun1()) |
| 165 | + task2 = asyncio.create_task(fun2()) |
| 166 | + |
| 167 | + ret1 = await task1 |
| 168 | + ret2 = await tssk2 |
| 169 | + Task对象不是阻塞的, await将会把这两个Task对象同时提交到任务循环中 |
| 170 | + |
143 | 171 |
|
144 | 172 | 2) 运行协程的三种主要方式 |
145 | 173 | (1) 用asyncio.run()函数用来运行. |
|
0 commit comments