Go语言多路复用,所有的goroutine都处于休眠状态 – 死锁

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

Go lang multiplexing all goroutines are asleep - deadlock

问题

我想在这里使用多个Go协程创建一个fan-in函数,返回一个通道。以下是我的代码。

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
}

但是在运行上述代码后,我遇到了一个错误:

所有的goroutine都处于休眠状态 - 死锁

我尝试过关闭通道,但仍然出现错误。我尝试将boring返回的通道分配给Joe和Ann,然后将这些通道传递给talk函数进行多路复用,但仍然没有成功。我是Go语言学习者,对通道的概念还不太清楚。

英文:

I wants to create a fan-in function using multiple go routines returning channel here is my code.

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

var wg, wg2 sync.WaitGroup

func main() {
	final := talk(boring(&quot;Joe&quot;), boring(&quot;Ann&quot;))
	for i := 0; i &lt; 10; i++ {
		fmt.Println(&lt;-final)
	}
	fmt.Println(&quot;You are both boring I&#39;m leaving&quot;)
}

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

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

But I got an error after running above code

> all goroutines are asleep - deadlock

I have tried to close channels but still it is giving me the error. I have tried to assign boring returned channels to Joe and Ann and then pass those channels to talk function for multiplexing still no success. I am new to go learning channels not clear on this concept.

答案1

得分: 2

替代使用等待组(wait groups),你可以使用select语句:https://tour.golang.org/concurrency/5

select语句允许一个goroutine等待多个通信操作。

select语句会阻塞,直到其中一个case可以执行,然后执行该case。如果有多个case都准备好了,它会随机选择一个。

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
}

在Playground上尝试

编辑:

在你给出的示例中,boring函数没有使用goroutine进行重复的通道发送,这将导致阻塞,因为:

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

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

我通过进行上述更改使其正常工作:https://play.golang.org/p/YN0kfBO6iT

英文:

Instead of wait groups, you can use select: https://tour.golang.org/concurrency/5

> The select statement lets a goroutine wait on multiple communication
> operations.
>
> A select blocks until one of its cases can run, then it executes that
> case. It chooses one at random if multiple are ready.
>
>

package main

import (
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;time&quot;
)

func main() {
	final := talk(boring(&quot;Joe&quot;), boring(&quot;Ann&quot;))
	for i := 0; i &lt; 10; i++ {
		fmt.Println(&lt;-final)
	}
	fmt.Println(&quot;You are both boring I&#39;m leaving&quot;)
}

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

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

Try it on Playground

Edit:

In your given example, boring function doesn't use goroutine for repeated send over channel which will block forever, because:
https://tour.golang.org/concurrency/2

> By default, sends and receives block until the other side is ready.
> This allows goroutines to synchronize without explicit locks or
> condition variables.

Also, wg.Done() needs to be part of goroutine.

I got it working by doing above changes: https://play.golang.org/p/YN0kfBO6iT

答案2

得分: 1

你不能让一个 goroutine 自己停止,我建议使用类似以下方式向 goroutine 发送信号以退出:

stop := make(chan bool)
go func() {
    for {
        select {
        case <-stop:
            return
        default:
            // 做其他的事情
        }
    }
}()

// 做其他的事情

// 退出 goroutine
stop <- true
英文:

You cannot stop a goroutine by itself, I suggest signaling the goroutine to quit with something along these lines:

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

// Do stuff

// Quit goroutine
stop&lt;- true

huangapple
  • 本文由 发表于 2017年6月12日 03:29:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/44487887.html
匿名

发表评论

匿名网友

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

确定