go lang多路复用所有goroutine都睡着了 - 死锁

时间:2017-06-11 19:29:12

标签: go

我想创建一个使用多个go例程的扇入函数返回通道这里是我的代码。

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

var wg, wg2 sync.WaitGroup

func main() {
    final := talk(boring("Joe"), boring("Ann"))
    for i := 0; i < 10; i++ {
        fmt.Println(<-final)
    }
    fmt.Println("You are both boring I'm leaving")
}

func talk(input1, input2 <-chan string) <-chan string {
    out := make(chan string)
    go func() {
        wg.Add(1)
        for {
            out <- <-input1
        }
    }()
    go func() {
        wg.Add(1)
        for {
            out <- <-input2
        }
    }()
    wg.Done()
    close(out)
    return out
}

func boring(msg string) <-chan string {
    c := make(chan string)
    for i := 0; i < 5; i++ {
        c <- fmt.Sprintf("%s%d\n", msg, i)
        time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
    }
    return c
}

但是在运行代码

之后我遇到了错误
  

所有goroutines都睡着了 - 僵局

我试图关闭频道,但它仍然给我错误。我试图将无聊的返回频道分配给Joe和Ann,然后将这些频道传递给用于多路复用的通话功能仍然没有成功。我是新手,学习这个概念的渠道不明确。

2 个答案:

答案 0 :(得分:2)

您可以使用selecthttps://tour.golang.org/concurrency/5

代替等待组
  

select语句允许goroutine等待多次通信   操作

     

select阻塞,直到其中一个案例可以运行,然后执行该案例   案件。如果多个准备就绪,它会随机选择一个。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    final := talk(boring("Joe"), boring("Ann"))
    for i := 0; i < 10; i++ {
        fmt.Println(<-final)
    }
    fmt.Println("You are both boring I'm leaving")
}

func talk(input1, input2 <-chan string) <-chan string {
    c := make(chan string)
    go func() {
        for {
            select {
            case s := <-input1:
                c <- s
            case s := <-input2:
                c <- s
            }
        }
    }()
    return c
}

func boring(msg string) <-chan string {
    c := make(chan string)
    go func() {
        for i := 0; i < 5; i++ {
            c <- fmt.Sprintf("%s: %d", msg, i)
            time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond)
        }
    }()
    return c
}

Try it on Playground

修改

在您给出的示例中,boring函数不会使用goroutine重复发送通道,这将永远阻止,因为: https://tour.golang.org/concurrency/2

  

默认情况下,发送和接收阻止,直到另一方准备好。   这允许goroutine在没有显式锁定的情况下进行同步   条件变量。

此外,wg.Done()需要成为goroutine的一部分。

我通过以上更改来实现它:https://play.golang.org/p/YN0kfBO6iT

答案 1 :(得分:1)

你不能单独阻止goroutine,我建议用goroutine发出信号,要求戒掉这些东西:

stop := make(chan bool)
go func() {
    for {
        select {
        case <- stop:
            return
        default:
            // Do other stuff
        }
    }
}()

// Do stuff

// Quit goroutine
stop<- true