34

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

4

3 回答 3

39

如果你删除了,time.Sleep你就不会给say("world")goroutine 运行的机会。goroutine 调度器不是抢占式的。你的 goroutine 必须在另一个 goroutine 运行之前放弃控制。放弃控制的一种方法是奔跑time.Sleep

如果你time.Sleepsay函数中取出 ,那么主 goroutine 运行 5 次而不放弃对辅助 goroutine 的控制,然后当主 goroutine 从say程序返回时退出,因为没有什么可以让程序保持活力。

于 2013-04-02T18:27:12.493 回答
13

因为 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 时,您可能永远不需要担心调度程序的内部结构。

于 2013-04-04T21:01:00.420 回答
2

如果你从 say 函数中删除time.Sleep,main 将执行 say("hello") 并终止而不执行 goroutine。如果您在主端之前添加 time.Sleep (或其他选择 {}),它将给 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但它也可以设法在最后一个hello之前打印一个world

试试看 --> (http://) goo.gl/K2v7H0

于 2016-06-07T23:36:23.560 回答