为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
温温酱 2022-07-11 15:03:05
因此,我在学习 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.goStartingWaiting to finish100 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这是应该的工作方式,我没有正确理解某些东西,还是我的代码不正确?
查看完整描述

2 回答

?
qq_笑_17

TA贡献1818条经验 获得超7个赞

编写代码时我必须做些什么,

不。

还是全部由调度程序处理?

是的。

在下面的示例中,我有两个使用 go 关键字在单独的 Go 例程中运行的函数。因为默认的 GOMAXPROCS 是您机器上可用的处理器数量(我也明确设置了它)我希望这两个函数同时运行

他们可能会也可能不会,你在这里无法控制。

因此输出将是特定顺序的数字混合 - 此外,每次运行时输出都会不同。然而,这种情况并非如此。相反,它们一个接一个地运行,更令人困惑的是,功能二在功能一之前运行。

是的。同样,您不能强制并行计算。

你的测试是有缺陷的:你只是在每个 goroutine 中做的不多。在您的示例中,goroutine 2 可能被安排在 goroutine 1 开始运行之前运行、开始运行并完成。“启动”一个 goroutinego并不强制它立即开始执行,所做的只是创建一个可以运行的新 goroutine。从所有可以运行的 goroutine 中,一些被调度到你的处理器上。所有这些调度都无法控制,它是全自动的。如您所知,这是并发和并行之间的区别。您可以控制 Go 中的并发性,但不能(太多)控制在两个或更多内核上实际并行完成的工作。

具有实际工作的长期运行的 goroutine 的更实际示例将显示交错输出。


查看完整回答
反对 回复 2022-07-11
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

这一切都由调度程序处理。


只有 20 条短指令的两个循环,您将很难看到并发或并行的效果。


这是另一个玩具示例: https: //play.golang.org/p/xPKITzKACZp


package main


import (

    "fmt"

    "runtime"

    "sync"

    "sync/atomic"

    "time"

)


const (

    ConstMaxProcs  = 2

    ConstRunners   = 4

    ConstLoopcount = 1_000_000

)


func runner(id int, wg *sync.WaitGroup, cptr *int64) {

    var times int

    for i := 0; i < ConstLoopcount; i++ {

        val := atomic.AddInt64(cptr, 1)

        if val > 1 {

            times++

        }

        atomic.AddInt64(cptr, -1)

    }


    fmt.Printf("[runner %d] cptr was > 1 on %d occasions\n", id, times)

    wg.Done()

}


func main() {

    runtime.GOMAXPROCS(ConstMaxProcs)


    var cptr int64


    wg := &sync.WaitGroup{}

    wg.Add(ConstRunners)


    start := time.Now()

    for id := 1; id <= ConstRunners; id++ {

        go runner(id, wg, &cptr)

    }


    wg.Wait()

    fmt.Printf("completed in %s\n", time.Now().Sub(start))

}

与您的示例一样:您无法控制调度程序,此示例只是有更多“表面”来见证并发的一些影响。


很难从程序内部看到并发性和并行性之间的实际差异,您可以在处理器运行时查看处理器的活动,或检查全局执行时间。


Playground 的时钟不提供亚秒级精度,如果您想查看实际时间,请将代码复制/粘贴到本地文件中并调整常量以查看各种效果。


请注意,其他一些影响(可能:if val > 1 {...}检查的分支预测和/或共享变量周围的内存失效cptr)使我的机器上的执行非常不稳定,所以不要指望直接“使用 ConstMaxProcs = 4 运行比快 4 倍ConstMaxProcs = 1"。


查看完整回答
反对 回复 2022-07-11
  • 2 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号