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

时间:2015-12-30 15:05:25

标签: multithreading go concurrency goroutine

我在模拟考试中遇到两个问题。我得到了答案,但无法弄清楚背后的理由。

我将首先发布代码,然后发布问题和答案。也许有人会这么好解释我的答案?

package main

import "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)

}

第一个问题是:

如果省略行&#34; d&lt; - 0&#34;程序如何表现?在主程序中,为什么?

老师的回答是:

所有线程的状态都被阻止,也是主线程。

第二个问题是:

如果我们交换事实程序的前两行,整个计划的效率如何受到影响?

答案是:

所有线程都将按顺序运行。每个线程只有在完成时才会触发另一个线程。

2 个答案:

答案 0 :(得分:10)

最好不要将其视为“多线程”。 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。什么将允许继续?来自d的另一个goroutine读物。请记住,我们开始N goroutines,所有人都试图从d读取。只有其中一个会成功。其他人将阻止(1),等待d上显示的内容。当第一个到达(2)时会发生这种情况。然后那个goroutine退出,随机goroutine将继续。

但是会有一个永远无法写入d的最终goroutine,它会泄漏。为了解决这个问题,需要在最终Printf

之前添加以下内容
<-d

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

答案 1 :(得分:4)

  

如果我们在主程序中省略“d&lt; - 0”行,该程序如何表现?为什么?

由于该行,go fact(...)开始的每个goroutine都会等待来自频道的内容,在z := <- d语句处被屏蔽。

fact()函数对d频道的内容没有净影响 - 删除了某些内容并添加了一些内容。因此,如果频道中没有任何内容,则不会有任何进展,程序将会死锁。

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

  

如果我们交换事实程序的前两行,整个计划的效率如何受到影响?

fact()例程将等待它从d频道获取一个令牌,然后再进行冗长的因子计算。

因为d频道中只有一个令牌同时存在,这意味着每个go例程只会在收到令牌时进行昂贵的计算,从而有效地序列化它们。

与最初一样,在等待令牌之前,并行进行昂贵的因子计算。

在实践中,由于goroutine没有先发制人地安排,只能阻止操作和函数调用,因此这种方法的效果不如您所希望的那么好。