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
php01 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
接收是原子的,接收+打印不是
|