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

Go 中的多线程。有人可以向我解释这些答案吗?

Go 中的多线程。有人可以向我解释这些答案吗?

C#
繁星点点滴滴 2021-12-06 15:00:31
我在模拟考试中遇到了两个问题。我得到了答案,但无法弄清楚它们背后的原理。我将首先发布代码,然后发布问题和答案。也许有人会这么好心地向我解释答案?package mainimport "fmt"func fact(n int, c chan int, d chan int) {    k := /* code to compute factorial of n */    z := <- d    c <- k + z    d <- z + 1}func main() {    r := 0    c := make(chan int)    d := make(chan int)    for i = 0 ; i < N ; i++ {        go fact(i,c,d)    }    d <- 0    for j = 0 ; j < N ; j++ {        r = r + <-c    }    fmt.Printf("result = %d\n",r)}第一个问题是:如果我们省略主程序中的“d <- 0”行,程序会怎样,为什么?老师的回答是:所有线程和主线程的状态都被阻塞。第二个问题是:如果交换fact过程的前两行,对整个程序的效率有什么影响?答案是:所有线程都将按顺序运行。每个线程只有在完成时才会触发另一个线程。
查看完整描述

2 回答

?
GCT1015

TA贡献1827条经验 获得超4个赞

最好不要将其视为“多线程”。Go 为并发提供了直接的便利,而不是为线程提供了便利。它碰巧使用线程来实现其并发性,但这是一个实现细节。参见 Rob Pike 的演讲,Concurrency is not parallelism进行更深入的讨论。


您问题的关键是默认情况下通道是同步的(如果它们在构建过程中没有被缓冲)。当一个 goroutine 写入一个通道时,它会阻塞,直到另一个 goroutine 从该通道读取。所以当这一行执行时:


z := <- d

在此行执行之前,它无法继续:


d <- 0

如果d频道上没有可用的价值,fact则永远不会继续。这对你来说很明显。但反过来也是如此。在从d通道读取某些内容之前,主 goroutine 无法继续。通过这种方式,无缓冲通道提供了跨并发 goroutine 的同步点。


同样,主循环无法继续,直到某个值出现在 上c。我发现使用两个手指并指向每个 goroutine 中的当前代码行很有用。前进一根手指,直到进入频道操作。然后推进另一个,直到它到达一个通道操作。如果您的手指指向同一通道上的读取和写入,则您可以继续。如果他们不是,那么你就陷入了僵局。


如果你仔细考虑一下,你就会发现一个问题。该程序泄漏了一个 goroutine。


func fact(n int, c chan int, d chan int) {

    k := /* code to compute factorial of n */

    z := <- d // (1)

    c <- k + z

    d <- z + 1 // (2)

}

在 (2) 处,我们尝试写入d. 什么将允许它继续进行?另一个 goroutine 从d. 请记住,我们启动了Ngoroutines,并且它们都试图从d. 只有其中之一会成功。其他人将在 (1) 处阻塞,等待某些东西出现在 上d。当第一个到达(2)时会发生这种情况。然后那个 goroutine 退出,一个随机的 goroutine 将继续。


但是会有一个最终的 goroutine 永远无法写入d并且会泄漏。为了解决这个问题,需要在 final 之前添加以下内容Printf:


<-d

这将允许最后一个 goroutine 退出。


查看完整回答
反对 回复 2021-12-06
?
繁华开满天机

TA贡献1816条经验 获得超4个赞

如果我们省略主程序中的“d <- 0”行,程序会怎样,为什么?

有了这行代码,每个 goroutine 开始go fact(...)都会等待来自 channel 的东西,在 statement 阻塞z := <- d

fact()函数对d频道的内容没有实际影响- 它删除一些内容并添加一些内容。因此,如果通道中没有任何内容,则不会有任何进展,程序将陷入僵局。

从同一通道读取和写入的 goroutine 要求死锁 - 在现实生活中避免!

如果交换fact过程的前两行,对整个程序的效率有什么影响?

在进行冗长的阶乘计算之前,fact()例程将等到它从d通道中获得令牌。

因为d通道中一次只有一个令牌在播放,这意味着每个 goroutine 只会在收到令牌时进行昂贵的计算,从而有效地将它们序列化。

与最初一样,昂贵的阶乘计算是在等待令牌之前并行完成的。

在实践中,这不会像您希望的那样工作,因为 goroutine 不是预先调度的,仅用于阻塞操作和函数调用。


查看完整回答
反对 回复 2021-12-06
  • 2 回答
  • 0 关注
  • 165 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信