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

C 操作系统线程是否在go例程执行的io上被阻塞?,c,multithreading,go,goroutine,C,Multithreading,Go,Goroutine,在我的机器上有4个逻辑处理器。因此,有四种上下文P1,P2,P3,P4使用操作系统线程M1,M2,M3,M4 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 4 On-line CPU(s) list: 0-3 Thread(s) per core: 2 Core(s) per socke

在我的机器上有4个逻辑处理器。因此,有四种上下文
P1
P2
P3
P4
使用操作系统线程
M1
M2
M3
M4

$ lscpu
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              4
On-line CPU(s) list: 0-3
Thread(s) per core:  2
Core(s) per socket:  2
Socket(s):           1

在以下代码中:

package main

import (
    "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)
    }

}
主程序包
进口(
“fmt”
“io/ioutil”
“net/http”
)
func getPage(url字符串)(int,错误){
resp,err:=http.Get(url)
如果错误!=零{
返回0,错误
}
延迟响应主体关闭()
body,err:=ioutil.ReadAll(resp.body)
如果错误!=零{
返回0,错误
}
返回len(主体),无
}

func-worker(urlChan-chan-string,sizeChan-chan不,它不会阻塞。我的粗略理解是(没有来源,我是通过渗透获得的)每当goroutine想要执行具有等效非阻塞版本的“阻塞”I/O时

  • 改为执行非阻塞版本
  • 将其自己的ID记录在由其“阻止”的句柄键入的表中的某个位置
  • 将完成的责任转移到位于
    select
    循环(或
    poll
    或任何可用的等效循环)中的专用线程,等待此类操作解除阻塞,以及
  • 挂起自身,释放其OS线程(M)以运行另一个goroutine
  • 当I/O操作解除阻塞时,select循环在表中查找哪个goroutine对结果感兴趣,并安排运行它。这样,等待I/O的goroutine就不会占用OS线程


    如果I/O无法以非阻塞方式完成,或任何其他阻塞系统调用,goroutine将通过运行时函数执行系统调用,该运行时函数将其线程标记为阻塞,运行时将为goroutine创建一个新的OS线程,以便在其上调度goroutine。这将保持GOMAXPROCS运行(未阻塞)的能力goroutines。这不会导致大多数程序的线程膨胀,因为处理文件、套接字等的最常见系统调用都是异步友好的。(感谢@JimB提醒我这一点,以及链接答案的作者。)

    使用
    GODEBUG=schedtrace=1000运行程序以查看goroutine调度跟踪,即当并发执行的用户线程仅限于GOMAXPROCS时,运行时将根据需要生成尽可能多的线程来服务阻塞系统调用,这当然包括任何实际的阻塞IO调用。另请参见,,,,等等ome阻塞IO调用,但它们被视为与任何其他阻塞系统调用相同;运行时根据需要生成新线程,以保持相同数量的用户线程运行。一点也不。在我上面链接的问题中可能也有更多的参考,我看得不太透彻。我们为上述读写器问题保留了带有信号量的关键部分lem使用C pthreads库。为什么我们不使用go例程进入关键部分?@overexchange:除了与手头的问题无关外,如果您在go中编写相同的代码,则需要使用相同的同步原语。但是,您编写的代码不同,因此比较实现细节是没有用的。@JimB当你说“在围棋中编写相同的代码”时,你的意思是不使用频道吗?