【问题标题】:When a goroutine blocks on I/O how does the scheduler identify that it has stopped blocking?当一个 goroutine 在 I/O 上阻塞时,调度程序如何识别它已经停止阻塞?
【发布时间】:2016-04-08 00:27:34
【问题描述】:

根据我阅读的here,golang 调度程序将自动确定一个 goroutine 是否在 I/O 上阻塞,并将自动切换到处理未阻塞线程上的其他 goroutine。

我想知道的是调度程序是如何计算出该 goroutine 已停止阻塞 I/O 的。

它是否只是经常进行某种轮询以检查它是否仍在阻塞?是否有某种后台线程正在运行来检查所有 goroutine 的状态?


例如,如果您要在一个 goroutine 中执行一个 HTTP GET 请求,该请求需要 5 秒才能获得响应,它会在等待响应时阻塞,并且调度程序将切换到处理另一个 goroutine。现在考虑到,当服务器返回响应时,调度程序如何理解响应已经到达,是时候回到发出 GET 的 goroutine 以便它可以处理 GET 的结果了?

【问题讨论】:

    标签: multithreading go concurrency


    【解决方案1】:

    所有的 I/O 都必须通过 syscall 完成,而在 Go 中实现 syscall 的方式总是通过运行时控制的代码来调用。这意味着当您调用系统调用时,而不是直接调用它(从而将线程的控制权交给内核),运行时会收到您想要进行的系统调用的通知,并代表 goroutine 执行此操作。例如,这允许它执行非阻塞系统调用而不是阻塞系统调用(本质上是告诉内核,“请执行此操作,但不要在完成之前阻塞,而是立即返回,并在结果后通知我准备好了”)。这允许它在此期间继续做其他工作。

    【讨论】:

    • 我想我已经理解了这部分内容。我真正好奇的是这部分“在读取结果后让我知道”是如何完成的。它如何检查是否“读取结果”
    • 通常使用信号。因此,当 IO 完成时,内核将调用您指定的某些特定函数,并且该函数可以随心所欲地处理它。
    • 首先,从系统调用的角度来看,IO 比您描述的更原始。例如,一个 GET 请求包括向内核请求来自 TCP 流的字节,直到整个请求被读取(这些字节将以块的形式被请求,例如一次 1024 个字节)。调度器不会对“这是一个 GET 请求”的高级逻辑有任何概念,而是只会知道“我们请求了 1024 字节,现在内核已经说它们终于准备好了”。然后将发起请求的 goroutine 放回运行队列,以便安排稍后运行。
    • (本质上,阻塞或“进入睡眠状态”只是意味着一个 goroutine 从可用的 goroutine 池中删除,当调度器做出调度决定时,它可以运行下一个。“被唤醒备份”只是意味着被放回那个游泳池)
    • 内核实际上自己执行 IO。进程自己只能访问 CPU——除此之外的任何内容基本上都是进程在说:“亲爱的内核,请代表我执行以下操作。”这包括访问任何外部设备(硬盘驱动器、键盘、显示器、网卡等),甚至与其他进程交互。
    猜你喜欢
    • 2017-09-12
    • 1970-01-01
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-06
    • 1970-01-01
    相关资源
    最近更新 更多