死锁与双通道

huangapple go评论162阅读模式
英文:

deadlock with double channel

问题

我正在尝试创建一个程序,将字符串发送到一组goroutine(通过通道)。一旦goroutine完成它们的工作,它们会通过另一个通道发送一些结果。

以下是代码:

package main

import "fmt"
import "os"
import "sync"
import "bufio"

func worker(linkChan <-chan string, outChan chan<- string, wg *sync.WaitGroup, jobId int) {
   defer wg.Done()

   for url := range linkChan {
    // ...
     outChan <- url
   }
}

func main() {
    lCh := make(chan string)
    wg := new(sync.WaitGroup)
    outCh := make(chan string)

    urls := []string{}
    if len(os.Args) > 1 {
        for _, link := range os.Args[1:] {
            urls = append(urls, link)
        }
    } else {
        s := bufio.NewScanner(os.Stdin)
        for s.Scan() {
            urls = append(urls, s.Text())
        }
    }

    num_worker := 10

    for i := 0; i < num_worker; i++ {
        wg.Add(1)
        go worker(lCh, outCh, wg, i)
    }
    for _, link := range urls {
        lCh <- link
    }
    close(lCh)

    for res := range outCh {
        fmt.Printf("%s\n", res)
    }
    close(outCh)
    wg.Wait()

}

运行 echo "something" | ./main 会导致死锁。

根据我理解,close(lCh) 应该停止 for url := range linkChan 循环。我错了吗(因为代码发生了死锁)?

我该如何解决这个死锁问题?

谢谢你的回答。

英文:

I'm trying to create a program that send strings to a pool of goroutines (through a channel). Once the goroutine have finish their job, they send some results (through an other channel).

The code is:

package main

import &quot;fmt&quot;
import &quot;os&quot;
import &quot;sync&quot;
import &quot;bufio&quot;

func worker(linkChan &lt;-chan string, outChan chan&lt;- string, wg *sync.WaitGroup, jobId int) {
   defer wg.Done()

   for url := range linkChan {
    // ...
     outChan &lt;- url
   }
}

func main() {
    lCh := make(chan string)
    wg := new(sync.WaitGroup)
    outCh := make(chan string)

    urls := []string{}
    if len(os.Args) &gt; 1 {
        for _, link := range os.Args[1:] {
            urls = append(urls, link)
        }
    } else {
        s := bufio.NewScanner(os.Stdin)
        for s.Scan() {
            urls = append(urls, s.Text())
        }
    }

    num_worker := 10

    for i := 0; i &lt; num_worker; i++ {
        wg.Add(1)
        go worker(lCh, outCh, wg, i)
    }
    for _, link := range urls {
        lCh &lt;- link
    }
    close(lCh)

    for res := range outCh {
        fmt.Printf(&quot;%s\n&quot;, res)
    }
    close(outCh)
    wg.Wait()

}

Running echo &quot;something&quot; | ./main cause a deadlock.

From what I've understood, close(lCh) should stop the for url := range linkChan loop. Am I wrong (it seems so since the code deadlock) ?

How can I resolve this deadlock ?

Thank you for your answers.

答案1

得分: 4

你需要在goroutine中处理URL,否则outCh会填满,因为你没有清空它。这将导致所有的工作线程都被阻塞,最终造成死锁。

所以重新调整代码如下:

go func() {
    for _, link := range urls {
        lCh <- link
    }
    close(lCh)
    wg.Wait()
    close(outCh)
}()

for res := range outCh {
    fmt.Printf("%s\n", res)
}

这样就可以正常工作了。

完整代码

英文:

You need to pump the urls in a goroutine, otherwise the outCh will fill up which as you aren't emptying it. This will stall all the workers and it will deadlock.

So re-arrange the code to look like this

go func() {
	for _, link := range urls {
		lCh &lt;- link
	}
	close(lCh)
	wg.Wait()
	close(outCh)
}()

for res := range outCh {
	fmt.Printf(&quot;%s\n&quot;, res)
}

And it will work fine

Complete code

答案2

得分: 0

你在关闭 outCh 之前使用了 range。你需要在 wg.Wait() 之后关闭 outCh,就像 Nick 的回答中那样。

英文:

https://golang.org/ref/spec#For_range :
> For channels, the iteration values produced are the successive values sent on the channel until the channel is closed. If the channel is nil, the range expression blocks forever.

You use range before close outCh. You have to close outCh after wg.Wait(), like in Nick`s answer.

huangapple
  • 本文由 发表于 2017年5月8日 04:14:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/43836206.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定