在GO教程中,我们有这张幻灯片:Goroutines
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
运行此代码会产生预期的结果(“world”和“hello”可互换地写入屏幕5次。)
但是,如果我们注释掉time.Sleep
(以及导入的"time"
行)并再次运行该程序,我们只剩下 “hello”写了五次屏幕。
time.Sleep
对于拯救goroutine免于死亡有什么重要意义?
答案 0 :(得分:34)
如果您删除time.Sleep
,则不会给say("world")
goroutine一个机会。 goroutine调度程序不是抢占式的。你的goroutines必须在另一个goroutine运行之前放弃控制。放弃控制权的一种方法是运行time.Sleep
。
如果从time.Sleep
函数中取出say
,则主goroutine运行5次而不放弃对辅助goroutine的控制,然后当主goroutine从say
返回时程序退出,因为没有什么可以保持程序活着。
答案 1 :(得分:9)
因为goroutine调度程序不是抢占式的,所以你的goroutine必须在另一个goroutine运行之前放弃控制。放弃控制权的一种方法是time.Sleep
。另一种方法是使用runtime.Gosched()
。
以下是修改为使用Gosched()的教程:http://play.golang.org/p/jQ9mlGYXXE
这是理解goroutines的有用教训。但是,试图直接控制调度程序绝对是一种反模式;悲伤往往会随之而来。
相反,更多地考虑像通信数字硬件(状态机是一个很好的类比)的goroutines。最好了解goroutine所基于的Communicating Sequential Processes模型。在基于CSP的设计中,每个goroutine都有自己的私有状态,并交换消息以与其他goroutine的状态进行交互。消息的传递强制同步,调度程序使用该同步来确定哪些活动获取cpu时间以及将哪些内容放入等待队列。
当你以这种方式接近Go时,你可能永远不必担心调度程序内部。
答案 2 :(得分:1)
如果从say函数中删除 time.Sleep ,main将执行say(“hello”)并终止而不执行goroutine。如果在主结束之前添加time.Sleep(或者 select {} ),它将为goroutine提供运行时间,并且将从调度程序中选择该线程。
示例:
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
// time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
time.Sleep(1*time.Second)
// Vs:
// select {} // blocks indefinitely, requires manual interrupt
// In CSP-speak the empty select is like STOP.
// for{} would cause the cpu to max and the process's STATE will be `running`
// select{} will not cause the cpu to max and the process state will be `sleeping`
}
输出通常是5 hello ,然后是5 world ,但它也可以设法打印最后一个 world 之一< EM>你好
尝试 - &gt; (http://)goo.gl/K2v7H0