所有克雷克 comments 的汇编和整理.
抢占式意味着内核(运行时)允许线程运行特定的时间量,然后在其他线程不做任何事情或不知道任何事情的情况下执行它们.在操作系统内核中,这通常是使用硬件中断实现的.进程不能挡路整个操作系统.在协作多任务中,线程必须显式地将执行交给其他线程.如果不这样做,它可能会挡路整个过程,甚至整台机器.围棋就是这样做的.它有一些非常具体的点,在那里Goroutine可以产生处决.但是,如果goroutine只为{}执行,那么它将锁定整个进程.
但是,这句话并没有提到运行时的最新变化.fmt.Println(sum)
可能会导致其他Goroutine被调度,因为较新的运行时将在函数调用上调用调度程序.
If you don't have any function calls, just some math, then yes, goroutine will lock the thread until it exits or hits something that could yield execution to others. That's why for {}
doesn't work in Go. Even worse, it will still lead to process hanging even if GOMAXPROCS > 1 because of how GC works, but in any case you shouldn't depend on that. It's good to understand that stuff but don't count on it. There is even a proposal to insert scheduler calls in loops like yours
Go的运行时所做的主要事情是,它尽最大努力让每个人都可以执行,而不会让任何人挨饿.它如何做到这一点没有在语言规范中指定,并且在future 可能会改变.如果将实现关于循环的建议,那么即使没有函数调用,也可能发生切换.目前,您唯一应该记住的是,在某些情况下,函数调用可能会导致goroutine执行.
为了解释Akavall答案中的切换,当调用fmt.Printf
时,它做的第一件事是判断是否需要增加堆栈并调用调度器.它可能会切换到另一条猩猩路由.它是否会切换取决于其他Goroutine的状态和调度器的确切实现.与任何调度器一样,它可能会判断是否有应该执行的饥饿Goroutines.对于多次迭代,函数调用有更大的机会进行切换,因为其他函数的饥饿时间更长.由于迭代次数很少,猩猩在饥饿发生之前就结束了.