刚刚我在网上冲浪,发现了这个一个问题,于是我想了一下,给了以下几种方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
func fun1() {
bagA := make(chan struct{})
bagB := make(chan struct{})
bagC := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
<-bagA
println("A")
bagB <- struct{}{}
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
<-bagB
println("B")
bagC <- struct{}{}
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
<-bagC
println("C")
bagA <- struct{}{}
}
}()
go func() {
bagA <- struct{}{}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
cancel()
}
|
这段代码还可以简化成这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
func fun2() {
bagA := make(chan struct{})
bagB := make(chan struct{})
bagC := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
bagB <- <-bagA
println("A")
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
bagC <- <-bagB
println("B")
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
bagA <- <-bagC
println("C")
}
}()
go func() {
bagA <- struct{}{}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
cancel()
}
|
还可以再简化一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
func fun2a() {
bagA := make(chan struct{})
bagB := make(chan struct{})
bagC := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
run := func(to, from chan struct{}, v string) {
for {
select {
case <-ctx.Done():
return
default:
}
to <- <-from
println(v)
}
}
go run(bagB, bagA, "A")
go run(bagC, bagB, "B")
go run(bagA, bagC, "C")
go func() {
bagA <- struct{}{}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
cancel()
}
|
还有没有别的办法呢?我想了一下,还可以利用 sync.Cond
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
func func3() {
mu := sync.Mutex{}
cond := sync.NewCond(&mu)
value := "A"
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
cond.L.Lock()
for value != "A" {
cond.Wait()
}
println("A")
value = "B"
cond.L.Unlock()
cond.Broadcast()
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
cond.L.Lock()
for value != "B" {
cond.Wait()
}
println("B")
value = "C"
cond.L.Unlock()
cond.Broadcast()
}
}()
go func() {
for {
select {
case <-ctx.Done():
return
default:
}
cond.L.Lock()
for value != "C" {
cond.Wait()
}
println("C")
value = "A"
cond.L.Unlock()
cond.Broadcast()
}
}()
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
cancel()
}
|
还可以更精简一些:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
func func3() {
mu := sync.Mutex{}
cond := sync.NewCond(&mu)
value := 'A'
ctx, cancel := context.WithCancel(context.Background())
run := func(id rune) {
for {
select {
case <-ctx.Done():
return
default:
}
cond.L.Lock()
for value != 'A'+id {
cond.Wait()
}
println(string(value))
value = 'A' + (id+1)%3
cond.L.Unlock()
cond.Broadcast()
}
}
go run(0)
go run(1)
go run(2)
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop
cancel()
}
|
想了想,这样写没问题吗, 会有 goroutine 泄露吗?
嗯,不会
因为在 cancel()之后, 肯定会有一个改变了 value 的 g0 最先退出,然后 value 再变一次,g1 退出,g2 接下来都也会退出