为什么在运行某些goroutine时需要使用time.sleep?

huangapple go评论87阅读模式
英文:

Why is time.sleep required to run certain goroutines?

问题

在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死亡?

英文:

In the GO tutorial, we have this slide: Goroutines

package main

import (
    &quot;fmt&quot;
    &quot;time&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;)
    say(&quot;hello&quot;)
}

Running this code produces expected results ("world" and "hello" written to the screen interchangeably 5 times).

However, if we comment out time.Sleep (and consequently, the &quot;time&quot; line of the import) and run the program again, we are left with only "hello" written to the screen five times.

What is so important about time.Sleep that saves the goroutine from dying?

答案1

得分: 41

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

如果你从say函数中移除了time.Sleep,那么主goroutine会连续运行5次而不将控制权交给次要的goroutine,然后当主goroutine从say返回时,程序会退出,因为没有任何东西可以使程序保持活动状态。

英文:

If you remove the time.Sleep you don't give the say(&quot;world&quot;) goroutine a chance to run. The goroutine scheduler is not preemptive. Your goroutines have to give up control before another goroutine will run. One way to give up control is to run time.Sleep.

If you take out the time.Sleep from the say function then the primary goroutine runs 5 times without giving up control to the secondary goroutine and then when the primary goroutine returns from say the program exits because there is nothing to keep the program alive.

答案2

得分: 13

因为goroutine调度器不是抢占式的,所以在另一个goroutine运行之前,你的goroutines必须放弃控制权。放弃控制权的一种方式是使用time.Sleep。另一种方式是使用runtime.Gosched()

这是一个使用Gosched()修改的教程:http://play.golang.org/p/jQ9mlGYXXE

这是一个理解goroutines的有用课程。然而,试图直接控制调度器绝对是一个反模式;通常会带来麻烦。

相反,更多地将goroutines视为通信数字硬件的块(状态机是一个很好的类比)。最好学习一下基于通信顺序进程模型的goroutines。在基于CSP的设计中,每个goroutine都有自己的私有状态,并通过交换消息与其他goroutine的状态进行交互。消息的传递强制进行同步,调度器使用它来确定哪个活动获得CPU时间,哪个活动被放入等待队列。

当你以这种方式使用Go时,你可能永远不需要担心调度器的内部机制。

英文:

Because the goroutine scheduler is not preemptive, your goroutines have to give up control before another goroutine will run. One way to give up control is with time.Sleep. Another way is with runtime.Gosched().

Here's the tutorial modified to use Gosched(): http://play.golang.org/p/jQ9mlGYXXE

This is a useful lesson in understanding goroutines. However, trying to control the scheduler directly is definitely an anti-pattern; grief will often follow.

Instead, think more about the goroutines like chunks of communicating digital hardware (state machines are a good analogy). It's better to learn about the Communicating Sequential Processes model on which goroutines are based. In a CSP-based design, each goroutine has its own private state and exchanges messages to interact with the state of other goroutines. The passing of messages forces synchronization, which the scheduler uses to determine what activity gets cpu time and what gets put in a wait queue.

When you approach Go this way, you probably never need to worry about scheduler internals.

答案3

得分: 2

如果你从say函数中删除_time.Sleep_,主函数将执行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,但也有可能在最后一个hello之前打印一个world。

TRY IT --> (http://) goo.gl/K2v7H0

英文:

If you remove time.Sleep from the say function the main will execute say("hello") and terminate without executing the goroutine. If you add a time.Sleep (or otherwise a select {}) before the main end it will give time to the goroutine to run and that thread will be picked from the scheduler.

Example:

package main

import (
    &quot;fmt&quot;
    &quot;time&quot;
)

func say(s string) {
    for i := 0; i &lt; 5; i++ {
        // time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say(&quot;world&quot;)
    say(&quot;hello&quot;)
    
    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&#39;s STATE will be `running`
          // select{} will not cause the cpu to max and the process state will be `sleeping`
}

The output will usually be 5 hello followed by 5 world but it could also manage to print one of the world before the last hello

TRY IT --> (http://) goo.gl/K2v7H0

huangapple
  • 本文由 发表于 2013年4月3日 02:11:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/15771232.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定