omr*_*umi 55 concurrency go goroutine
在调用系统调用时,其他goroutine如何继续执行?(当使用GOMAXPROCS = 1时)
据我所知,在调用系统调用时,线程会放弃控制,直到系统调用返回为止.Go如何在不使用block-on-syscall goroutine创建系统线程的情况下实现这种并发性?
从文档:
够程
它们被称为goroutines,因为现有的术语 - 线程,协同程序,进程等 - 传达了不准确的内涵.goroutine有一个简单的模型:它是一个与同一地址空间中的其他goroutine同时执行的函数.它是轻量级的,比堆栈空间的分配花费更多.并且堆栈开始很小,因此它们很便宜,并且通过根据需要分配(和释放)堆存储来增长.
Goroutines被多路复用到多个OS线程上,因此如果应该阻塞,例如在等待I/O时,其他线程继续运行.他们的设计隐藏了线程创建和管理的许多复杂性.
One*_*One 37
如果goroutine正在阻塞,则运行时将启动一个新的OS线程来处理其他goroutine,直到阻塞的一个停止阻塞.
参考:https://groups.google.com/forum/#!topic/golang -nuts/IdA34yR8gQ
omr*_*umi 27
好的,所以这就是我所学到的:当你进行原始系统调用时,Go确实会为每个阻塞goroutine创建一个线程.例如,请考虑以下代码:
package main
import (
"fmt"
"syscall"
)
func block(c chan bool) {
fmt.Println("block() enter")
buf := make([]byte, 1024)
_, _ = syscall.Read(0, buf) // block on doing an unbuffered read on STDIN
fmt.Println("block() exit")
c <- true // main() we're done
}
func main() {
c := make(chan bool)
for i := 0; i < 1000; i++ {
go block(c)
}
for i := 0; i < 1000; i++ {
_ = <-c
}
}
Run Code Online (Sandbox Code Playgroud)
在运行它时,Ubuntu 12.04为该进程报告了1004个线程.
另一方面,当使用Go的HTTP服务器并向其打开1000个套接字时,只创建了4个操作系统线程:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Run Code Online (Sandbox Code Playgroud)
因此,它是IOLoop和每个阻塞系统调用的线程之间的混合.
nos*_*nos 13
它不能.当GOMAXPROCS = 1时,只有一个goroutine可以运行,无论是一个goroutine正在进行系统调用还是其他什么.
但是,从Go执行的系统调用时,大多数阻塞系统调用(例如套接字I/O)等待计时器都不会被阻止.它们由Go运行时复用到OS为多路复用I/O提供的epoll,kqueue或类似工具.
对于无法与epoll之类的多路复用的其他类型的阻塞系统调用,Go确实产生了一个新的OS线程,无论GOMAXPROCS设置如何(尽管这是Go 1.1中的状态,我不确定情况是否已更改)
您必须区分处理器编号和线程编号:您可以拥有比物理处理器更多的线程,因此多线程进程仍然可以在单个核心处理器上执行.
正如你引用的文档解释的那样,goroutine不是一个线程:它只是一个在一个专用于一大堆堆栈空间的线程中执行的函数.如果您的进程有多个线程,则此函数可由任一线程执行.因此,可以在其线程中放入因某种原因而阻塞的goroutine(系统调用,I/O,同步),而其他例程可以由另一个执行.