看到https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/14.7.md文章中有讲到基于锁和基于管道的任务的实现区别。自己实现了下。
主要目的是了解go中用selet { case ok<- : do() case stop<- : return } 和 if v,ok:= <-chanA 两种从管道中读取数据的区别。
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var stopFlag chan int = make(chan int)
pending, done := make(chan *TaskA, 1), make(chan *TaskA, 1)
go sending(pending, stopFlag, done)
for i := 0; i < 3; i++ {
go working(pending, done, i, stopFlag)
}
consumeDone(done, stopFlag)
close(pending)
close(stopFlag)
fmt.Println("end")
}
type TaskA struct {
value int
}
func sending(pending chan *TaskA, stopFlag chan int, done chan *TaskA) {
temps := make([]*TaskA, 0)
for i := 0; i < 10; i++ {
temp := &TaskA{i}
temps = append(temps, temp)
pending <- temp
fmt.Printf("send value %d\n", i)
time.Sleep(time.Duration(int64(rand.Intn(3)) * int64(time.Second)))
}
fmt.Printf("%v\n", temps)
fmt.Printf("%v\n", temps[1]) // 这里可以尝试把,所有chan *TaskA都改成chan TaskA 用以观察值传递和指针传递的区别
time.Sleep(time.Second * 3)
stopFlag <- 1 // 有几个用到stopFlag的地方就要出发几次(是一种实验测试的实现 比较硬编码)
stopFlag <- 1
stopFlag <- 1
close(done)
stopFlag <- 1
time.Sleep(time.Second * 1)
}
func working(in chan *TaskA, out chan *TaskA, index int, stop chan int) {
for {
select {
case <-stop:
fmt.Println("return from working")
return
case inChan := <-in:
inChan.value = -inChan.value
out <- inChan
fmt.Printf("worker %d consume value %d\n", index, inChan.value)
}
}
}
func consumeDone(done chan *TaskA, stop chan int) {
for {
if d, ok := <-done; ok { // 如果 done没有关闭 就会一直阻塞在这里 所以下边判断stop是否关闭的语句不会执行
// 如果是 双chan 实现 do or stop 的模型 还是用 select 吧
fmt.Printf("consumeDone value %d\n", d)
} else if _, ok2 := <-stop; ok2 {
fmt.Printf("return from consumeDone\n")
return
}
}
}