它是并发的,但是是什么让它并行运行呢?

因此,我在学习 Go 的同时试图了解并行计算的工作原理。我理解并发性和并行性之间的区别,但是,我有点坚持的是 Go(或操作系统)如何确定应该并行执行的东西......


编写代码时我必须做些什么,还是全部由调度程序处理?


在下面的示例中,我有两个使用 go 关键字在单独的 Go 例程中运行的函数。因为默认的 GOMAXPROCS 是您机器上可用的处理器数量(我也明确设置了它)我希望这两个函数同时运行,因此输出将是特定顺序的数字混合 - 并且此外,每次运行时输出都会不同。然而,这种情况并非如此。相反,它们一个接一个地运行,更令人困惑的是,功能二在功能一之前运行。


代码:


func main() {

    runtime.GOMAXPROCS(6)

    var wg sync.WaitGroup

    wg.Add(2)


    fmt.Println("Starting")

    go func() {

        defer wg.Done()

        for smallNum := 0; smallNum < 20; smallNum++ {

            fmt.Printf("%v ", smallNum)

        }

    }()


    go func() {

        defer wg.Done()

        for bigNum := 100; bigNum > 80; bigNum-- {

            fmt.Printf("%v ", bigNum)

        }

    }()


    fmt.Println("Waiting to finish")

    wg.Wait()


    fmt.Println("\nFinished, Now terminating")

}

输出:


go run main.go

Starting

Waiting to finish

100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 

Finished, Now terminating

我正在关注这篇文章,尽管我遇到的几乎每个示例都做类似的事情。 并发、Goroutines 和 GOMAXPROCS


这是应该的工作方式,我没有正确理解某些东西,还是我的代码不正确?


温温酱
浏览 143回答 2
2回答

qq_笑_17

编写代码时我必须做些什么,不。还是全部由调度程序处理?是的。在下面的示例中,我有两个使用 go 关键字在单独的 Go 例程中运行的函数。因为默认的 GOMAXPROCS 是您机器上可用的处理器数量(我也明确设置了它)我希望这两个函数同时运行他们可能会也可能不会,你在这里无法控制。因此输出将是特定顺序的数字混合 - 此外,每次运行时输出都会不同。然而,这种情况并非如此。相反,它们一个接一个地运行,更令人困惑的是,功能二在功能一之前运行。是的。同样,您不能强制并行计算。你的测试是有缺陷的:你只是在每个 goroutine 中做的不多。在您的示例中,goroutine 2 可能被安排在 goroutine 1 开始运行之前运行、开始运行并完成。“启动”一个 goroutinego并不强制它立即开始执行,所做的只是创建一个可以运行的新 goroutine。从所有可以运行的 goroutine 中,一些被调度到你的处理器上。所有这些调度都无法控制,它是全自动的。如您所知,这是并发和并行之间的区别。您可以控制 Go 中的并发性,但不能(太多)控制在两个或更多内核上实际并行完成的工作。具有实际工作的长期运行的 goroutine 的更实际示例将显示交错输出。

婷婷同学_

这一切都由调度程序处理。只有 20 条短指令的两个循环,您将很难看到并发或并行的效果。这是另一个玩具示例: https: //play.golang.org/p/xPKITzKACZppackage mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "runtime"&nbsp; &nbsp; "sync"&nbsp; &nbsp; "sync/atomic"&nbsp; &nbsp; "time")const (&nbsp; &nbsp; ConstMaxProcs&nbsp; = 2&nbsp; &nbsp; ConstRunners&nbsp; &nbsp;= 4&nbsp; &nbsp; ConstLoopcount = 1_000_000)func runner(id int, wg *sync.WaitGroup, cptr *int64) {&nbsp; &nbsp; var times int&nbsp; &nbsp; for i := 0; i < ConstLoopcount; i++ {&nbsp; &nbsp; &nbsp; &nbsp; val := atomic.AddInt64(cptr, 1)&nbsp; &nbsp; &nbsp; &nbsp; if val > 1 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; times++&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; atomic.AddInt64(cptr, -1)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Printf("[runner %d] cptr was > 1 on %d occasions\n", id, times)&nbsp; &nbsp; wg.Done()}func main() {&nbsp; &nbsp; runtime.GOMAXPROCS(ConstMaxProcs)&nbsp; &nbsp; var cptr int64&nbsp; &nbsp; wg := &sync.WaitGroup{}&nbsp; &nbsp; wg.Add(ConstRunners)&nbsp; &nbsp; start := time.Now()&nbsp; &nbsp; for id := 1; id <= ConstRunners; id++ {&nbsp; &nbsp; &nbsp; &nbsp; go runner(id, wg, &cptr)&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Printf("completed in %s\n", time.Now().Sub(start))}与您的示例一样:您无法控制调度程序,此示例只是有更多“表面”来见证并发的一些影响。很难从程序内部看到并发性和并行性之间的实际差异,您可以在处理器运行时查看处理器的活动,或检查全局执行时间。Playground 的时钟不提供亚秒级精度,如果您想查看实际时间,请将代码复制/粘贴到本地文件中并调整常量以查看各种效果。请注意,其他一些影响(可能:if val > 1 {...}检查的分支预测和/或共享变量周围的内存失效cptr)使我的机器上的执行非常不稳定,所以不要指望直接“使用 ConstMaxProcs = 4 运行比快 4 倍ConstMaxProcs = 1"。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go