|
| 1 | +--- |
| 2 | +title: 为什么更好 |
| 3 | +date: 2025-01-10 08:28:00 |
| 4 | +author: loongs-zhang |
| 5 | +--- |
| 6 | + |
| 7 | +# 为什么更好 |
| 8 | + |
| 9 | +[English](../en/why-better.md) | 中文 |
| 10 | + |
| 11 | +## 系统调用不会阻塞 |
| 12 | + |
| 13 | +首先,我们来看一下线程是如何与系统调用协作的。 |
| 14 | + |
| 15 | +```mermaid |
| 16 | +sequenceDiagram |
| 17 | + actor 用户线程 |
| 18 | + participant 操作系统 |
| 19 | + 用户线程 ->>+ 用户线程: 执行 |
| 20 | + alt 用户线程被阻塞 |
| 21 | + 用户线程 ->>+ 操作系统: 慢系统调用 |
| 22 | + 操作系统 ->> 用户线程: 返回 |
| 23 | + end |
| 24 | + 用户线程 ->>+ 用户线程: 执行 |
| 25 | +``` |
| 26 | + |
| 27 | +如果系统调用是一个慢系统调用,例如默认阻塞的`accept`,线程将被长时间阻塞,直到操作系统返回为止,期间无法做任何事情。现在,我们来看一下 open-coroutine 是如何与系统调用协作的。 |
| 28 | + |
| 29 | +```mermaid |
| 30 | +sequenceDiagram |
| 31 | + actor EventLoop线程 |
| 32 | + participant 协程1 |
| 33 | + participant 协程2 |
| 34 | + participant 被代理的系统调用 |
| 35 | + participant 操作系统 |
| 36 | + EventLoop线程 ->>+ 协程1: 调度 |
| 37 | + alt 协程1逻辑上被阻塞 |
| 38 | + 协程1 ->>+ 被代理的系统调用: 慢系统调用 |
| 39 | + 被代理的系统调用 ->>+ 操作系统: 快系统调用 |
| 40 | + 操作系统 ->> 被代理的系统调用: 返回错误码 |
| 41 | + 被代理的系统调用 ->> 协程1: 挂起协程一段时间 |
| 42 | + end |
| 43 | + 协程1 ->>+ EventLoop线程: 挂起 |
| 44 | + EventLoop线程 ->>+ 协程2: 调度 |
| 45 | + alt 协程2逻辑上被阻塞 |
| 46 | + 协程2 ->>+ 被代理的系统调用: 慢系统调用 |
| 47 | + 被代理的系统调用 ->>+ 操作系统: 快系统调用 |
| 48 | + 操作系统 ->> 被代理的系统调用: 返回 |
| 49 | + 被代理的系统调用 ->> 协程2: 返回 |
| 50 | + end |
| 51 | + 协程2 ->>+ EventLoop线程: 返回 |
| 52 | + EventLoop线程 ->>+ 协程1: 调度 |
| 53 | + alt 协程1逻辑上被阻塞 |
| 54 | + 协程1 ->>+ 被代理的系统调用: 从上次暂停处恢复 |
| 55 | + 被代理的系统调用 ->>+ 操作系统: 快系统调用 |
| 56 | + 操作系统 ->> 被代理的系统调用: 返回 |
| 57 | + 被代理的系统调用 ->> 协程1: 返回 |
| 58 | + end |
| 59 | + 协程1 ->>+ EventLoop线程: 返回 |
| 60 | + EventLoop线程 ->>+ EventLoop线程: 调度其他协程 |
| 61 | +``` |
| 62 | + |
| 63 | +如你所见,`被代理的系统调用`(hook)将`慢系统调用`转换为`快系统调用`。通过这种方式,尽管`EventLoop线程`在执行系统调用时仍然会被阻塞,但阻塞时间非常短。因此,与线程模型相比,`EventLoop线程`可以在相同的时间内做更多的事情。 |
| 64 | + |
| 65 | +## 重度计算不会阻塞 |
| 66 | + |
| 67 | +其次,我们来看一下线程如何处理重度计算。 |
| 68 | + |
| 69 | +```mermaid |
| 70 | +sequenceDiagram |
| 71 | + actor 用户线程 |
| 72 | + alt 用户线程陷入循环 |
| 73 | + 用户线程 ->>+ 用户线程: 执行循环 |
| 74 | + end |
| 75 | +``` |
| 76 | + |
| 77 | +就像上面的系统调用一样,线程会一直阻塞在循环中。接下来,我们来看一下open-coroutine如何处理重度计算。 |
| 78 | + |
| 79 | +```mermaid |
| 80 | +sequenceDiagram |
| 81 | + actor EventLoop线程 |
| 82 | + participant 协程1 |
| 83 | + participant 协程2 |
| 84 | + participant Monitor |
| 85 | + EventLoop线程 ->>+ 协程1: 调度 |
| 86 | + alt 协程1进入循环 |
| 87 | + 协程1 ->>+ 协程1: 执行循环一段时间 |
| 88 | + Monitor ->> 协程1: 挂起协程 |
| 89 | + end |
| 90 | + 协程1 ->>+ EventLoop线程: 挂起 |
| 91 | + EventLoop线程 ->>+ 协程2: 调度 |
| 92 | + alt 协程2进入循环 |
| 93 | + 协程2 ->>+ 协程2: 执行循环一段时间 |
| 94 | + Monitor ->> 协程1: 挂起协程 |
| 95 | + end |
| 96 | + 协程2 ->>+ EventLoop线程: 挂起 |
| 97 | + EventLoop线程 ->>+ 协程1: 调度 |
| 98 | + alt 协程1进入循环 |
| 99 | + 协程1 ->>+ 协程1: 从上次暂停处恢复 |
| 100 | + end |
| 101 | + 协程1 ->>+ EventLoop线程: 返回 |
| 102 | + EventLoop线程 ->>+ EventLoop线程: 调度其他协程 |
| 103 | +``` |
| 104 | + |
| 105 | +`Monitor`会监控协程的执行情况,一旦发现某个协程的执行时间过长,就会强制挂起该协程。因此,现在我们甚至可以[使用一个`EventLoop线程`来执行多个循环](https://github.com/loongs-zhang/open-coroutine/blob/master/open-coroutine/examples/preemptive.rs),这是单线程模型无法实现的。 |
0 commit comments