在执行工作之前,我一直遵循一种检查通道中是否有东西的模式:
func consume(msg <-chan message) {
for {
if m, ok := <-msg; ok {
fmt.Println("More messages:", m)
} else {
break
}
}
}
基于此video的。这是我的完整代码:
package main
import (
"fmt"
"strconv"
"strings"
"sync"
)
type message struct {
body string
code int
}
var markets []string = []string{"BTC", "ETH", "LTC"}
// produces messages into the chan
func produce(n int, market string, msg chan<- message, wg *sync.WaitGroup) {
// for i := 0; i < n; i++ {
var msgToSend = message{
body: strings.Join([]string{"market: ", market, ", #", strconv.Itoa(1)}, ""),
code: 1,
}
fmt.Println("Producing:", msgToSend)
msg <- msgToSend
// }
wg.Done()
}
func receive(msg <-chan message, wg *sync.WaitGroup) {
for {
if m, ok := <-msg; ok {
fmt.Println("Received:", m)
} else {
fmt.Println("Breaking from receiving")
break
}
}
wg.Done()
}
func main() {
wg := sync.WaitGroup{}
msgC := make(chan message, 100)
defer func() {
close(msgC)
}()
for ix, market := range markets {
wg.Add(1)
go produce(ix+1, market, msgC, &wg)
}
wg.Add(1)
go receive(msgC, &wg)
wg.Wait()
}
如果您尝试运行它,则在打印将要中断的消息之前,我们最终将陷入僵局。自上次以来,当chan中没有其他内容时,tbh才有意义,因此我们试图拉出该值,因此出现此错误。但是,此模式if m, ok := <- msg; ok
不可行。如何使此代码正常工作以及为什么会出现此死锁错误(大概该模式应该起作用?)。
答案 0 :(得分:1)
鉴于您在单个频道上确实有多个作者,因此您会遇到一些挑战,因为在Go中执行此操作的简单方法通常是在单个频道上拥有一个作者,然后再拥有一个作者编写者在发送最后一个数据后关闭通道:
func produce(... args including channel) {
defer close(ch)
for stuff_to_produce {
ch <- item
}
}
此模式具有很好的属性,无论您如何离开produce
,渠道都会关闭,这标志着生产结束。
您没有使用此模式-您向多个goroutine提供一个通道,每个goroutine可以发送一个消息-因此您需要移动close
(当然,也可以移动,请使用其他模式)。表达所需模式的最简单方法是:
func overall_produce(... args including channel ...) {
var pg sync.WaitGroup
defer close(ch)
for stuff_to_produce {
pg.Add(1)
go produceInParallel(ch, &pg) // add more args if appropriate
}
pg.Wait()
}
pg
计数器会累积活跃的生产者。每个人都必须调用pg.Done()
来表明它是使用ch
完成的。现在,整个制作人都在等待所有操作完成,然后 it 在退出频道时将其关闭。
(如果您将内部produceInParallel
函数作为闭包编写,则无需显式传递ch
和pg
。也可以编写overallProducer
作为关闭。)
请注意,使用for ... range
构造可以最好地表达单个消费者的循环:
func receive(msg <-chan message, wg *sync.WaitGroup) {
for m := range msg {
fmt.Println("Received:", m)
}
wg.Done()
}
(您提到了向循环中添加select
的意图,以便在消息尚未准备好时可以执行其他一些计算。如果该代码无法分解为独立的goroutine,则实际上需要更高级的m, ok := <-msg
构造。)
还请注意,wg
的{{1}}(根据您构造其他事物的方式可能是不必要的)与等待组receive
完全独立,生产者。诚然,按照书面说明,只有在所有生产者都完成之后才能完成消费者的工作,但我们希望独立等待生产者完成,以便我们可以关闭整体生产者包装中的渠道。>
答案 1 :(得分:0)
尝试此代码,我做了一些修复使其有效:
package main
import (
"fmt"
"strconv"
"strings"
"sync"
)
type message struct {
body string
code int
}
var markets []string = []string{"BTC", "ETH", "LTC"}
// produces messages into the chan
func produce(n int, market string, msg chan<- message, wg *sync.WaitGroup) {
// for i := 0; i < n; i++ {
var msgToSend = message{
body: strings.Join([]string{"market: ", market, ", #", strconv.Itoa(1)}, ""),
code: 1,
}
fmt.Println("Producing:", msgToSend)
msg <- msgToSend
// }
}
func receive(msg <-chan message, wg *sync.WaitGroup) {
for {
if m, ok := <-msg; ok {
fmt.Println("Received:", m)
wg.Done()
}
}
}
func consume(msg <-chan message) {
for {
if m, ok := <-msg; ok {
fmt.Println("More messages:", m)
} else {
break
}
}
}
func main() {
wg := sync.WaitGroup{}
msgC := make(chan message, 100)
defer func() {
close(msgC)
}()
for ix, market := range markets {
wg.Add(1)
go produce(ix+1, market, msgC, &wg)
}
go receive(msgC, &wg)
wg.Wait()
fmt.Println("Breaking from receiving")
}
答案 2 :(得分:0)
仅当main
返回时,您才能close(msgC)
,但是与此同时receive
正在等待close
信号,这就是发生死锁的原因。产生消息后,关闭频道。
package main
import (
"fmt"
"strconv"
"strings"
"sync"
)
type message struct {
body string
code int
}
var markets []string = []string{"BTC", "ETH", "LTC"}
// produces messages into the chan
func produce(n int, market string, msg chan<- message, wg *sync.WaitGroup) {
// for i := 0; i < n; i++ {
var msgToSend = message{
body: strings.Join([]string{"market: ", market, ", #", strconv.Itoa(1)}, ""),
code: 1,
}
fmt.Println("Producing:", msgToSend)
msg <- msgToSend
// }
wg.Done()
}
func receive(msg <-chan message, wg *sync.WaitGroup) {
for {
if m, ok := <-msg; ok {
fmt.Println("Received:", m)
} else {
fmt.Println("Breaking from receiving")
break
}
}
wg.Done()
}
func main() {
wg := sync.WaitGroup{}
msgC := make(chan message, 100)
// defer func() {
// close(msgC)
// }()
for ix, market := range markets {
wg.Add(1)
go produce(ix+1, market, msgC, &wg)
}
wg.Wait() // wait for producer
close(msgC)
wg.Add(1)
go receive(msgC, &wg)
wg.Wait()
}