golang 多个协程中 读取同一个 channel,怎么不是按顺序打印

2021-01-22 11:26:17 +08:00
 hyp1002950

func main(){

ch := make(chan int)

go func(){
	for{
		c :=<-ch
		fmt.Println("one:",c,"len:",len(ch))
	}
}()

go func(){
	for{
		c :=<-ch
		fmt.Println("two:",c,"len:",len(ch))
	}
}()

for i:=1;i<=100;i++{
	ch<-i
}

time.Sleep(time.Second * time.Duration(2))

}

5012 次点击
所在节点    Go 编程语言
31 条回复
cloverzrg2
2021-01-22 11:31:13 +08:00
你把工作分给两个工人去做,工人 1 和工人 2 的完成的先后顺序是不确定的
jazzychai
2021-01-22 11:32:03 +08:00
因为它们是 X 个 goroutine 在执行
huobazi
2021-01-22 11:32:48 +08:00
按顺序用协程干啥?
beidounanxizi
2021-01-22 11:34:45 +08:00
读取 channel 的数据顺序是顺序读取的
但是你的 goroutine 运行顺序
由调度器调度的 不受你的控制
除非你能做到同步
hyp1002950
2021-01-22 11:38:17 +08:00
<-ch 不是会阻塞么,我想着它应该是在协程中接收到 1 打印出来了 然后才能接收到 2 再打印,现在给我感觉是 接收和打印不是一个原子操作
yzbythesea
2021-01-22 11:41:06 +08:00
先接受不一定先打印啊
yzbythesea
2021-01-22 11:41:22 +08:00
必然不是原子啊
cxh116
2021-01-22 11:41:31 +08:00
无脑猜 stdout print 应该算是 IO 操作, IO 操作时 goroutine 切换了.
zxlzy
2021-01-22 11:42:07 +08:00
ch<-i 后面 sleep 一下
hyp1002950
2021-01-22 11:52:10 +08:00
@cxh116 我觉得这个说法能想通,接收和打印应该是一起运行了,只是 print 是 IO 操作
hyp1002950
2021-01-22 11:57:42 +08:00
@zxlzy 我在 fmt.Println 之后 sleep 了,打印就 1-100 了
sadfQED2
2021-01-22 12:02:25 +08:00
你给你的管道缓冲长度设置成 1 就是顺序的了
sadfQED2
2021-01-22 12:03:52 +08:00
你没设置长度,所以 ch<-i 不会阻塞
towry
2021-01-22 13:49:36 +08:00
不能看不注重代码格式的人写的代码
no1xsyzy
2021-01-22 14:06:42 +08:00
@sadfQED2 https://play.golang.org/p/zoNa2QRR6F- 然而不是……
没设置长度的话我记得是没接收方就阻塞,有接收方转移到接收方
而且就算设置长度输出仍然是混乱的

似乎每个 goroutine 有单独的输出缓冲区,再用一个 channel 去集中的话就可以
https://play.golang.org/p/Xa-lwecfveV
但不是很懂没设置长度但有集中的情况下为什么还是乱的。
no1xsyzy
2021-01-22 14:26:49 +08:00
@no1xsyzy 可能不是单独缓冲区一说,用令牌方法保证两个 goroutine 交替运行以后立即保证了输出也是 one two 交替的……
而且 Println 应当会 flush ?
oluoluo
2021-01-22 16:59:13 +08:00
这里的 channel 是无缓冲通道,会阻塞的,所以值是按照顺序从 channel 中取出来的,但是取值和打印不是原子的,中间调度器发生调度导致打印的结果不是顺序的。
janxin
2021-01-22 17:03:05 +08:00
因为你 goroutine 调度不是顺序执行的
qq1340691923
2021-01-22 17:05:27 +08:00
package main

import (
"fmt"
"sync"
"time"
)

func main() {
loca := sync.Mutex{}
ch := make(chan int)

go func() {
for {
loca.Lock()
{
c := <-ch
fmt.Println("one:", c, "len:", len(ch))
}
loca.Unlock()

}
}()

go func() {
for {
loca.Lock()
{
c := <-ch
fmt.Println("two:", c, "len:", len(ch))
}
loca.Unlock()
}
}()

for i := 1; i <= 100; i++ {
ch <- i
}

time.Sleep(time.Second * time.Duration(2))
}


这样就好了
php01
2021-01-22 17:05:53 +08:00
接收到的顺序必然是按顺序来的,但是哪个协程先读到,或先写出,是控制不了的

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

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

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

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

© 2021 V2EX