为什么这段代码能够顺序的打印出 1-10,没太理解?

62 天前
 Grocker

问了 ds, 解释的有点 get 不到,求大佬详细解释下

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	prev := make(chan struct{})

	current := make(chan struct{})

	prev = current

	for id := 1; id <= 10; id++ {
		wg.Add(1)

		next := make(chan struct{})

		go func(id int, prev <-chan struct{}, next chan<- struct{}) {
			defer wg.Done()
			<-prev

			fmt.Println(id)

			if id < 10 {
				close(next)
			}
		}(id, prev, next)
		prev = next

	}
	close(current)
	wg.Wait()
}


3771 次点击
所在节点    Go 编程语言
9 条回复
LitterGopher
62 天前
關鍵在於 `next := make(chan struct{})` 這一行代碼, 每次循環都會創建一個新的 chan, 在閉包中只有等 prev 被寫入/或關閉的時候讀取纔會結束, 然後纔會繼續後面的內容, 然而這個 prev 每次都被 next 重新填充, 所以除了第一個 goroutine 後面的所有 goroutine 都是上一個循環創建的 next. 所以要等上一個 next 關閉或被寫入. 所以只有在第一個被寫入後才能依次出發後面的 goroutine 執行, 而每一個 goroutine 中 close(next) 實際上就是在對下一個 goroutine close(prev)
LitterGopher
62 天前
這寫法雖然有點意思, 但是不推薦這樣寫.
yuntun
62 天前
通过 prev <-chan struct{}和 next chan<- struct{} 的机制, id=1 的 goroutine 等待 current 被关闭,最先被唤醒;
然后打印 1 ,然后关闭 next , 也就是传给 id=2 的 prev, id=2 的 goroutine 被唤醒,打印 2 ,然后关闭他的 next , 整个链条顺序唤醒,顺序打印,直到 id=10 。
bronyakaka
61 天前
每个 goroutine 的 prev channel 是前一个 goroutine 的 next channel 。每个 goroutine 在执行打印之前会等待从 prev channel 接收信号,打印完成后会向 next channel 发送信号(通过关闭 channel )。
iceheart
61 天前
就像,烽火台
zxjxzj9
61 天前
就像接力棒,第一棒( current )在循环外交给了第一个 goroutine ,然后在每个 goroutine 里都把当前这个接力棒小给下一个 goroutine 后退出。程序干的事情实际是一口气开 10 个 goroutine (好比跑道上已经准备好了 10 个人),然后在循环外把第一个 channel 丢给了 id 是 1 的 goroutine ,之后依次往下接力
AkinoKaedeChan
60 天前
类似 PV 操作,把 close 看成 V 操作,把 <- 看成 P 操作,变量被反复修改了,其实可以看成共 10 个信号量。
zzhaolei
60 天前
击鼓传花。可以这么写,没必要创建一个新的 channel ,还需要回收:
```
prev := make(chan struct{})
start := prev
...

close(start)
```
minchieh
60 天前
基础知识:chan 类型变量是引用传递(子函数给赋值 会改变原值)
加入 2 行代码。真相就出来了
------------------------------------------------------------------
go func(id int, prev <-chan struct{}, next chan<- struct{}) {
defer wg.Done()
fmt.Printf("我是%d 我在等%p\n", id, prev)
<-prev

fmt.Println(id)

if id < 10 {
fmt.Printf("========我是%d 我放行了%p\n", id, next)
close(next)
}
}(id, prev, next)

--------------------------------------------------------------------------------
看打印:
初始的 prev:0x140000221c0
我是 10 我在等 0x140000225b0
我是 6 我在等 0x140000223f0
我是 7 我在等 0x14000022460
我是 8 我在等 0x140000224d0
我是 9 我在等 0x14000022540
我是 2 我在等 0x14000022230
我是 4 我在等 0x14000022310
我是 5 我在等 0x14000022380
我是 1 我在等 0x140000221c0
1
我是 3 我在等 0x140000222a0
========我是 1 我放行了 0x14000022230
2
========我是 2 我放行了 0x140000222a0
3
========我是 3 我放行了 0x14000022310
4
========我是 4 我放行了 0x14000022380
5
========我是 5 我放行了 0x140000223f0
6
========我是 6 我放行了 0x14000022460
7
========我是 7 我放行了 0x140000224d0
8
========我是 8 我放行了 0x14000022540
9
========我是 9 我放行了 0x140000225b0
10

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

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

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

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

© 2021 V2EX