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))
}
var mu sync.Mutex func Println(v ...interface{}){ mu.Lock() fmt.Println(v...) mu.Unlock() } func main(){ ch := make(chan int) go func(){ for{ c :=<-ch Println("one:",c,"len:",len(ch)) } }() go func(){ for{ c :=<-ch Println("two:",c,"len:",len(ch)) } }() for i:=1;i<=100;i++{ ch<-i } time.Sleep(time.Second * time.Duration(2)) } /* 输出 two: 1 len: 0 two: 3 len: 0 two: 4 len: 0 two: 5 len: 0 two: 6 len: 0 two: 7 len: 0 one: 2 len: 0 two: 8 len: 0 two: 10 len: 0 two: 11 len: 0 two: 12 len: 0 one: 9 len: 0 two: 13 len: 0 two: 15 len: 0 two: 16 len: 0 one: 14 len: 0 one: 18 len: 0 ... 结论:channel的接收和 之后的代码不是一起同时执行,不具有原子性,当channel被接收的时候,协程切换了 */
1 cloverzrg2 2021-01-22 11:31:13 +08:00 你把工作分给两个工人去做,工人 1 和工人 2 的完成的先后顺序是不确定的 |
![]() | 2 jazzychai 2021-01-22 11:32:03 +08:00 因为它们是 X 个 goroutine 在执行 |
![]() | 3 huobazi 2021-01-22 11:32:48 +08:00 按顺序用协程干啥? |
![]() | 4 beidounanxizi 2021-01-22 11:34:45 +08:00 读取 channel 的数据顺序是顺序读取的 但是你的 goroutine 运行顺序 由调度器调度的 不受你的控制 除非你能做到同步 |
5 hyp1002950 OP <-ch 不是会阻塞么,我想着它应该是在协程中接收到 1 打印出来了 然后才能接收到 2 再打印,现在给我感觉是 接收和打印不是一个原子操作 |
![]() | 6 yzbythesea 2021-01-22 11:41:06 +08:00 先接受不一定先打印啊 |
![]() | 7 yzbythesea 2021-01-22 11:41:22 +08:00 必然不是原子啊 |
![]() | 8 cxh116 2021-01-22 11:41:31 +08:00 无脑猜 stdout print 应该算是 IO 操作, IO 操作时 goroutine 切换了. |
9 zxlzy 2021-01-22 11:42:07 +08:00 ch<-i 后面 sleep 一下 |
10 hyp1002950 OP @cxh116 我觉得这个说法能想通,接收和打印应该是一起运行了,只是 print 是 IO 操作 |
11 hyp1002950 OP @zxlzy 我在 fmt.Println 之后 sleep 了,打印就 1-100 了 |
![]() | 12 sadfQED2 2021-01-22 12:02:25 +08:00 via Android 你给你的管道缓冲长度设置成 1 就是顺序的了 |
![]() | 13 sadfQED2 2021-01-22 12:03:52 +08:00 via Android 你没设置长度,所以 ch<-i 不会阻塞 |
![]() | 14 towry 2021-01-22 13:49:36 +08:00 不能看不注重代码格式的人写的代码 |
![]() | 15 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 但不是很懂没设置长度但有集中的情况下为什么还是乱的。 |
![]() | 16 no1xsyzy 2021-01-22 14:26:49 +08:00 @no1xsyzy 可能不是单独缓冲区一说,用令牌方法保证两个 goroutine 交替运行以后立即保证了输出也是 one two 交替的…… 而且 Println 应当会 flush ? |
![]() | 17 oluoluo 2021-01-22 16:59:13 +08:00 这里的 channel 是无缓冲通道,会阻塞的,所以值是按照顺序从 channel 中取出来的,但是取值和打印不是原子的,中间调度器发生调度导致打印的结果不是顺序的。 |
![]() | 18 janxin 2021-01-22 17:03:05 +08:00 因为你 goroutine 调度不是顺序执行的 |
19 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)) } 这样就好了 |
![]() | 20 anteros 2021-01-22 17:05:53 +08:00 接收到的顺序必然是按顺序来的,但是哪个协程先读到,或先写出,是控制不了的 |
![]() | 21 Nitroethane 2021-01-22 17:08:12 +08:00 @hyp1002950 #11 用 sleep 并不是完美的同步方法,虽然将 sleep 的时间设置的长一点可以在很大程度上得到顺序输出,但本质上由于 goroutine 调度器的原因还是会可能出现乱序输出情况,因为哪个 goroutine 先执行取决于调度器。正确的做法是用一个无 buffer 的 channel 去做两个 goroutine 之间的同步 |
![]() | 22 keepeye 2021-01-22 17:18:22 +08:00 用单核 cpu 可能得到顺序结果吧 |
![]() | 23 kele1997 2021-01-22 17:52:46 +08:00 ``` var lock1 sync.Mutex var lock2 sync.Mutex func main() { ch := make(chan int) lock2.Lock() go func() { for { lock1.Lock() c := <-ch fmt.Println("one:", c, "len:", len(ch)) lock2.Unlock() } }() go func() { for { lock2.Lock() c := <-ch fmt.Println("two:", c, "len:", len(ch)) lock1.Unlock() } }() for i := 1; i <= 100; i++ { ch <- i } time.Sleep(time.Second * time.Duration(2)) } ``` |
25 back0893 2021-01-22 19:09:25 +08:00 协程的调用你没有办法保证是顺序调用 |
26 zhyl 2021-01-22 20:32:11 +08:00 两个人在窗口取餐,谁先谁后肯定要争一争了。 |
![]() | 27 Takamine 2021-01-22 21:01:59 +08:00 via Android channel 只是顺序放在了一个 queue 里,多个 worker 每次谁去取不一定。 |
28 useben 2021-01-23 09:24:37 +08:00 底层是锁 |
29 kifile 2021-01-23 09:49:14 +08:00 为什么绿皮车比高铁发车早,但是比高铁到的晚呢,这就是原因 |
30 zhuzeitou 2021-01-23 10:16:14 +08:00 via Android 可以试试 go func 的最后 runtime.Gosched()一下 |
31 adamwong 2021-01-23 11:56:42 +08:00 接收是原子的,接收+打印不是 |