请教 goroutine 通信写法问题

2022-08-17 18:22:21 +08:00
 chaleaochexist

第一种想法如上图: main goroutine 遍历这个 chan, 将所有数据读出. 但是问题是. 左边那三个 goroutine 如果结束了. main goroutine 如何结束? 岂不是阻塞了? 也没法关闭, 如果左边的其中一个关闭了. 那另外两个的数据就读不出来了.

第二种思路是创建三个 chan

这个直接 close 就行了.

我的问题是: 第一种想法中的问题如何解决?

是不是实践中第二种想法比较符合套路?

确实是新手. 大佬勿喷.

2426 次点击
所在节点    Go 编程语言
35 条回复
wisej
2022-08-18 09:27:19 +08:00
@chaleaochexist 你对 waitgroup 的使用有误解,add 和 wait 都是生产者侧调用的。跟 consumer 没关系,consumer 只需要循环读取 chan 消费。

另外,如果 producer 并发很高,建议多个 chan ;毕竟 chan 底层依赖 mutex ,main 里再 for select 消费
index90
2022-08-18 09:35:32 +08:00
最简单就是加一个 waitgroup ,再起一个 goroutine 去 wait ,wait 到了就 close channel ,main routine 用 for range 去读。channel 。
index90
2022-08-18 09:37:25 +08:00
套路都是 main routine 去 range channel ,剩下的问题就是如何 close channel ,这个建议你搜索“如何优雅关闭 channel”,学习 channel 使用的几个套路。
ns09005264
2022-08-18 09:37:43 +08:00
```
ch := make(chan any)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(done func()) {
defer done()
time.Sleep(time.Second * 3)
ch <- rand.Int()
}(wg.Done)
}
go func() {
wg.Wait()
close(ch)
}()
for v := range ch {
log.Printf("v:%v", v)
}
log.Printf("这里等待执行")

```
ns09005264
2022-08-18 09:42:32 +08:00
joesonw
2022-08-18 09:58:54 +08:00
@ns09005264 Done 应该要放在消费者里,不然假如消费者处理时间比较长的,会漏掉最后一条。
joesonw
2022-08-18 10:00:26 +08:00
@ns09005264 忽略我上面说的,没看到是 unbuffered
BingoXuan
2022-08-18 10:51:02 +08:00
这个方案我研究过,头疼得很。最后选择手写 CAS 得 ringbuffer 的方案。
loveuer
2022-08-18 10:55:53 +08:00
第一种写法按照官方的说法就是不要过分关注 chan 的关闭了, 毕竟没有数据了, chan 的占用很小, 程序如果结束自然也就交还操作系统了
seth19960929
2022-08-18 11:18:26 +08:00
close chan 两个原则
1. 不要在接收端关闭, 也就是你代码里的 main goroutine
2. 有多个同时写, 不要在写的地方关闭, 也就是你的代码中 goroutine1,2,3

所以最好的做法, 就是楼上给的那个代码, 先 waitGroup 够三个之后, 直接在 main 关闭就行了
chaleaochexist
2022-08-18 11:24:18 +08:00
@index90
@ns09005264
谢谢 最后用的这个方法解决了.
seth19960929
2022-08-18 11:24:42 +08:00
package main

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

func main() {

// init var
ch := make(chan int)
wg := &sync.WaitGroup{}

// goroutine1, 2, 3
for i := 0; i < 3; i++ {
wg.Add(1)
go task(wg, ch)
}

// read chan data
go func() {
for val := range ch {
fmt.Println(val)
}
}()

// wait group
wg.Wait()
close(ch)

// close fast, can`t read all chan data
time.Sleep(time.Second)
}

func task(wg *sync.WaitGroup, ch chan int) {
defer wg.Done()

ts := rand.Intn(3) + 1
time.Sleep(time.Second * time.Duration(ts))

ch <- ts
}
lessMonologue
2022-08-18 11:35:44 +08:00
channel 为什么要关闭?
index90
2022-08-18 20:41:43 +08:00
这里可以延伸一个问题,你上述例子用了 4 个 goroutine ,如果用 waitgroup ,就要用 5 个 goroutine ,问能否用 3 个 goroutine 解决问题?
paceewang1
2022-08-31 19:24:19 +08:00
1 、chan 关闭了里面的数据可以继续读,只是不可以写
2 、多个 goroutine 关闭用 context

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/873544

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX