为了账号安全,请及时绑定邮箱和手机立即绑定

操作系统线程是否在 go-routine 执行的 io 上被阻塞?

操作系统线程是否在 go-routine 执行的 io 上被阻塞?

Go
拉风的咖菲猫 2022-06-01 17:29:35
在我的机器上有 4 个逻辑处理器。所以有四个上下文P1, P2, P3&P4使用 OS 线程M1, M2, M3&M4$ lscpuArchitecture:        x86_64CPU op-mode(s):      32-bit, 64-bitByte Order:          Little EndianCPU(s):              4On-line CPU(s) list: 0-3Thread(s) per core:  2Core(s) per socket:  2Socket(s):           1在下面的代码中:package mainimport (    "fmt"    "io/ioutil"    "net/http")func getPage(url string) (int, error) {    resp, err := http.Get(url)    if err != nil {        return 0, err    }    defer resp.Body.Close()    body, err := ioutil.ReadAll(resp.Body)    if err != nil {        return 0, err    }    return len(body), nil}func worker(urlChan chan string, sizeChan chan<- string, i int) {    for {        url := <-urlChan        length, err := getPage(url)        if err == nil {            sizeChan <- fmt.Sprintf("%s has length %d (%d)", url, length, i)        } else {            sizeChan <- fmt.Sprintf("%s has error %s (%d)", url, err, i)        }    }}func main() {    urls := []string{"http://www.google.com/", "http://www.yahoo.com",        "http://www.bing.com", "http://bbc.co.uk", "http://www.ndtv.com", "https://www.cnn.com/"}    urlChan := make(chan string)    sizeChan := make(chan string)    for i := 0; i < len(urls); i++ {        go worker(urlChan, sizeChan, i)    }    for _, url := range urls {        urlChan <- url    }    for i := 0; i < len(urls); i++ {        fmt.Printf("%s\n", <-sizeChan)    }}有六个执行例程http.Get()1)OS thread() 是否被io() 上M1的 go-routine() 阻塞?上下文G1http.Get()P1或者G1Go 调度程序是否会从 OS 线程(M1)抢占 go-routine( ) http.Get()?并分配G2给M1... 如果是,则在抢占 时G1,G1Goruntime如何G1在 IO() 完成后恢复http.Get?2)检索用于每个 go-routine(G) 的上下文编号 (P) 的 api 是什么?用于调试目的..3) 我们使用 C pthreads 库为上述读写器问题使用计数信号量维护关键部分。为什么我们不使用 go-routines 和通道来使用关键部分?
查看完整描述

1 回答

?
有只小跳蛙

TA贡献1824条经验 获得超8个赞

不,它不会阻塞。我粗略的(并且没有来源,我是通过 osmosis 得到的)理解是,每当一个 goroutine 想要执行一个具有等效非阻塞版本的“阻塞”I/O 时,

  1. 改为执行非阻塞版本。

  2. 将它自己的 ID 记录在一个由它“阻塞”的句柄键入的表中。

  3. 将完成的责任转移到一个专用线程,该线程位于select循环中(或poll任何可用的等效线程)等待此类操作解除阻塞,并且

  4. 挂起自身,释放其操作系统线程(M)以运行另一个 goroutine。

当 I/O 操作解除阻塞时,选择循环在表中查找哪个 goroutine 对结果感兴趣,并安排它运行。这样,等待 I/O 的 goroutine 就不会占用一个 OS 线程。

在无法以非阻塞方式完成的 I/O 或任何其他阻塞系统调用的情况下,goroutine 通过将其线程标记为阻塞的运行时函数执行系统调用,并且运行时将为 goroutines 创建一个新的 OS 线程以被安排在。这保持了让 GOMAXPROCS 运行(未阻塞)goroutines 的能力。对于大多数程序来说,这不会导致太多的线程膨胀,因为用于处理文件、套接字等的最常见的系统调用已经变得异步友好。(感谢@JimB 提醒我这一点,以及有用的链接答案的作者。)


查看完整回答
反对 回复 2022-06-01
  • 1 回答
  • 0 关注
  • 127 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信