runtime.LockOsThread
州的
LockOSThread将调用goroutine连接到其当前操作系统线程。在调用goroutine退出或调用UnlockOSThread之前,它将始终在该线程中执行,而没有其他goroutine可以执行。
但考虑一下这个程序:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
go fmt.Println("This shouldn't run")
time.Sleep(1 * time.Second)
}
main
goroutine连接到GOMAXPROCS
设置的一个可用操作系统线程,所以我希望在main
的第3行创建的goroutine不会运行。但是程序打印This shouldn't run
,暂停1秒,然后退出。为什么会这样?
答案 0 :(得分:6)
来自runtime
package documentation:
GOMAXPROCS变量限制了可以同时执行用户级Go代码的操作系统线程数。 代表Go代码在系统调用中可以阻止的线程数没有限制;那些不计入GOMAXPROCS限制。
睡眠线程不会计入GOMAXPROCS
值1,因此Go可以让另一个线程运行fmt.Println
goroutine。
答案 1 :(得分:1)
这是一个Windows示例,可能会帮助您了解正在发生的事情。它打印正在运行goroutine的线程ID。不得不使用系统调用,因此它只适用于Windows。但您可以轻松将其移植到其他系统。
package main
import (
"fmt"
"runtime"
"golang.org/x/sys/windows"
)
func main() {
runtime.GOMAXPROCS(1)
runtime.LockOSThread()
ch := make(chan bool, 0)
go func(){
fmt.Println("2", windows.GetCurrentThreadId())
<- ch
}()
fmt.Println("1", windows.GetCurrentThreadId())
<- ch
}
我不使用sleep来防止运行时产生另一个用于睡眠goroutine的线程。 Channel将阻止并从运行队列中删除goroutine。如果执行代码,您将看到线程ID不同。主goroutine锁定了其中一个线程,因此运行时必须生成另一个线程。
正如您所知,GOMAXPROCS
不会阻止运行时产生更多线程。 GOMAXPROCS
更多地是关于可以并行执行goroutine的线程数。但是,例如,可以为等待系统调用完成的goroutine创建更多线程。
如果删除runtime.LockOSThread()
,您将看到线程ID相等。这是因为通道读取会阻止goroutine并允许运行时将执行权交给另一个goroutine而不会生成新线程。即使GOMAXPROCS
为1,这也是多个goroutine可以同时执行的方式。
答案 2 :(得分:0)
这看起来对我来说是正确的行为。根据我的理解,LockOSThread()
函数只将所有未来的调用绑定到单个OS线程,它不会休眠或暂停线程。
为了清晰起见编辑:LockOSThread()
唯一做的是关闭多线程,以便所有未来的GO调用都在一个线程上发生。这主要用于需要单个线程正常工作的图形API等。
再次编辑。
如果你比较一下:
func main() {
runtime.GOMAXPROCS(6)
//insert long running routine here.
go fmt.Println("This may run almost straight away if tho long routine uses a different thread")
}
对此:
func main() {
runtime.GOMAXPROCS(6)
runtime.LockOSThread()
//insert long running routine here.
go fmt.Println("This will only run after the task above has completed")
}
如果我们在上面指出的位置插入一个长时间运行的例程,那么如果长例程在新线程上运行,则第一个块几乎可以直接运行,但在第二个例子中,它总是必须等待例程完成
答案 3 :(得分:0)
GOMAXPROCS(1)使您拥有一个ACTIVE M(OS线程)来执行go例程(G)。
您的程序中有两个Go例程,一个是main
,另一个是fmt.Println
。由于main
例程处于睡眠状态,因此M可以自由运行任何go例程,在这种情况下fmt.Println
可以运行。