为什么运行某些goroutine需要time.sleep?

时间:2013-04-02 18:11:15

标签: go goroutine

在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免于死亡有什么重要意义?

3 个答案:

答案 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