GMP获取可以执行的协程的过程

下面是对 findRunnable 函数执行流程的详细解释

  1. 从本地队列获取 (runqget(pp)):

    • 首先会尝试从当前的 P(即当前执行的处理器)上的本地运行队列获取一个可运行的 Goroutine。

  2. 检查是否有 trace 相关的 Goroutine (traceReader()):

    • 如果启用了 trace 或者 trace 正在关闭,会尝试调度一个 traceReader Goroutine。

  3. 尝试调度 GC 工作 (gcController.findRunnableGCWorker(pp, now)):

    • 如果启用了 GC(垃圾回收),并且当前有 GC 工作需要处理,会尝试调度 GC 工作。

  4. 检查全局队列

    • 检查全局队列: 每次运行的 P 都会偶尔检查全局队列。特别是当 pp.schedtick % 61 == 0 时,会从全局队列尝试获取一个 Goroutine,这样可以确保公平性,避免两个 Goroutine 占据本地队列进行不停的互相复生。

  5. 唤醒最后的 Goroutine(用于处理 finalizer)

    • 如果有 finalizer 需要处理,且它处于等待状态,则会尝试唤醒 finalizer Goroutine。

  6. Cgo 回调 (*cgo_yield):

    • 如果有 Cgo 回调,可能会调用 asmcgocall 来执行。

  7. 本地队列为空时

    • 如果本地队列没有可执行的 Goroutine,且全局队列也为空,会检查是否有网络事件(如网络 I/O)。

  8. 网络事件处理 (netpollinited()netpollAnyWaiters()):

    • 如果网络轮询已经初始化并且有等待事件,会尝试从网络事件中获取一个可执行的 Goroutine。

  9. 工作窃取

    • 如果没有其他 Goroutine 可运行,且 M 处于 spinning 状态,它会尝试从其他 P 的队列窃取工作。如果成功,会返回一个 Goroutine。

  10. 空闲 M:

  • 如果以上都没有得到可执行的 Goroutine,且当前是空闲状态,最后会通过 stopm() 来停止 M。

因此,主要的流程是按照本地队列 → 全局队列 → 网络事件 → 窃取工作 → 空闲状态的顺序来寻找可执行的 Goroutine。如果在这个过程中没有找到任何可以执行的任务,最终会让 M 进入空闲状态,等待下次工作分配。

你可能感兴趣的:(GMP,go1.19)