为什么我需要睡两次才能看到goroutines?

时间:2016-05-12 20:24:13

标签: go concurrency

我用go编写了这个小程序。关于go关键字,我只知道当我以这种方式调用函数时,会同时执行。我没有time.Sleep()尝试执行此代码,但没有生成输出。我需要添加time.Sleep(1000)两次。一次。睡眠声明是不够的。为什么呢?

package main

import (
    "fmt"
    "time"
)

func doSomething(integer int) {
    fmt.Println(integer)
}

func main() {
    i := 1

    for i <= 10 {
        go doSomething(i)
        i++
    }

    time.Sleep(1000)
    time.Sleep(1000)
}

2 个答案:

答案 0 :(得分:4)

那是因为当您使用go调用函数时(即作为goroutine),它将在后台运行并继续执行。在这种情况下,程序在调用goroutine之后没有任何内容,所以它只是完成而你看不到输出。你为什么需要两个?嗯,你不是,只是1秒的睡眠不够长,如果你有time.Sleep(2000)你可能得到相同的结果(虽然它&#39;不保证)。通常在使用goroutine时,需要某种阻塞语句。最简单的方法就是将一个通道传递给goroutine,然后从中接收。

由于代码的结构化,这不是最有用的示例,但这是程序的修改版本,它将阻塞直到第一个goroutine完成。由于您实际上正在调用10个goroutine,因此需要更强大的逻辑来阻塞直到它们完成,但更合理的解决方案是将循环放在gouroutine中,而不是调用10个goroutine。 https://play.golang.org/p/66g9u8Bhjj

另外,在go tour中查看这个更现实的例子; https://tour.golang.org/concurrency/2

答案 1 :(得分:1)

试试这个:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func doSomething(integer int) {
    fmt.Println(integer)
}

func main() {
    i := 1

    for i <= 10 {
        go doSomething(i)
        i++
    }
    runtime.Gosched()
    time.Sleep(10 * time.Millisecond)
}

它将以不同的方式运行。

你知道完成这个(你的)代码需要多长时间:
(考虑CPU加载100%,其他进程具有更高的优先级。)

func doSomething(integer int) {
    fmt.Println(integer)
}
for i <= 10 {
    go doSomething(i)
    i++
}

简单回答:没有。
这取决于每个系统中的许多未知因素:
1- CPU速度和CPU核心数和CPU缓存数...
2- RAM速度和总线速度和总线矩阵(它是并发的吗?)
2- OS调度程序和OS负载和优先级
3- Golang调度器
4 CPU负载
...

我的意思是: 在并发系统和并发编程中,依靠简单的代码(或汇编)指令时序并不是一个好主意。

是的,了解Golang调度程序以及它是如何工作的很好,但是 即使你知道Golang调度程序的行为,你知道操作系统现在在做什么吗?

仅在实时操作系统中估计某些已知时间在毫秒左右的范围内。

有时甚至更好地限制并发:
这是限制并发性的一种方法:

//using chan to limit the concurrency
package main

import (
    "os/exec"
    "runtime"
    "sync"
)

func main() {
    numberOfJobs := 10
    c := make(chan *exec.Cmd, numberOfJobs)
    for i := 0; i < numberOfJobs; i++ {
        c <- exec.Command("notepad")
    }
    close(c)

    nCPU := runtime.NumCPU()
    var wg sync.WaitGroup
    for i := 0; i < nCPU; i++ {
        wg.Add(1)
        go func() {
            for cmd := range c {
                cmd.Run()
            }
            wg.Done()
        }()
    }

    wg.Wait()
}

这是限制并发性的另一种方法:

//using chan to limit the concurrency
package main

import (
    "os/exec"
    "runtime"
    "sync"
)

func main() {
    n := runtime.NumCPU()
    dummy := make(chan bool, n)
    numberOfJobs := 10
    var wg sync.WaitGroup
    for i := 0; i < numberOfJobs; i++ {
        cmd := exec.Command("notepad")
        dummy <- true // wait here if no CPU available
        wg.Add(1)
        go func(cmd *exec.Cmd) {
            //defer func() { <-dummy }()
            cmd.Run()
            wg.Done()
            <-dummy
        }(cmd)
    }
    close(dummy)

    wg.Wait()
}

我希望这会有所帮助。