Go Channel
Channels
概念:通道Channels可被认为是Goroutines通信的管道。
声明:通道零值为nil(没有任何作用),故通道必须使用类似map和slice的方法来定义
a := make(chan int)
发送与接收
data := <- a // read from channel a eg: chan <- //发送数据
a <- data // write to channel a eg: <-cjan// 接收数据
一个通道发送、接收数据默认是阻塞的。故一个数据被发送到channel,在发送语句中被阻塞,直到另一个Goroutine
来接收。接收数据也类似。
通道的特性是帮助Goroutines有效地进行通信,而无需像使用其他编程语言中非常常见的显式锁或条件变量。
Channel的理论基础是CSP,Go语言的并发基于CSP模型。不要通过共享内存来通信,要通过通信来共享内存。
1、Channel基础
channel是goroutine之间交互,发一个数据必须有另一个goroutine去接收它。
例子1:channel发数据
func chanDemo(){ c := make(chan int) //这里是开一个goroutine去接受发送的 如果没有会报错 go func() { for { n := <-c fmt.Println(n) } }() c <- 1 c <- 2 time.Sleep(time.Millisecond)//① } func main() { chanDemo() //如果没有①则这里输出是1,因为虽然2发过去了但是来不及打印出来就结束了 }
类似函数一样,一等公民,可以作为参数也可作为返回值,可以随意的加参数。
注:chan<- 说明是用来发送数据的,<-chan说明是用来接收数据的
例子2:channel.go
重点涉及:channel、buffered channel、range
//打印收到的数据 func worker(id int, c chan int) { for n := range c { fmt.Printf("Worker %d received %c\n", id, n) } } //创建channel 然后将其返回出去 chan<- int 用来发送数据 func createWorker(id int) chan<- int { c := make(chan int) go worker(id, c) //里面的人用来收数据 return c } func chanDemo() { var channels [10]chan<- int for i := 0; i < 10; i++ { channels[i] = createWorker(i)//分发 } for i := 0; i < 10; i++ { //发送小a channels[i] <- 'a' + i } for i := 0; i < 10; i++ { channels[i] <- 'A' + i } time.Sleep(time.Millisecond) } func bufferedChannel() { c := make(chan int, 3)//channel发送之后必须有人收,这里缓冲区是3 go worker(0, c) c <- 'a' c <- 'b' c <- 'c' c <- 'd' time.Sleep(time.Millisecond)//必须要加这个要不然没有输出哦 } //告诉接收方我发完数据啦 func channelClose() { c := make(chan int) go worker(0, c) c <- 'a' c <- 'b' c <- 'c' c <- 'd' close(c) time.Sleep(time.Millisecond) } func main() { fmt.Println("Channel as first-class citizen") chanDemo() fmt.Println("Buffered channel") bufferedChannel() fmt.Println("Channel close and range") channelClose() }
输出是:
Channel as first-class citizen Worker 0 received a Worker 1 received b Worker 2 received c Worker 3 received d Worker 4 received e Worker 5 received f Worker 6 received g Worker 7 received h Worker 8 received i Worker 8 received I Worker 9 received j Worker 9 received J Worker 0 received A Worker 1 received B Worker 2 received C Worker 3 received D Worker 4 received E Worker 5 received F Worker 6 received G Worker 7 received H Buffered channel Worker 0 received a Worker 0 received b Worker 0 received c Worker 0 received d Channel close and range Worker 0 received a Worker 0 received b Worker 0 received c Worker 0 received d
2、通过通信来共享内存
使用Channel等待任务结束
func doWork(id int, w worker) { for n := range w.in { fmt.Printf("Worker %d received %c\n", id, n) w.done() //告诉别人我打印完啦 发送done } } type worker struct { in chan int done func() } func createWorker( id int, wg *sync.WaitGroup) worker { w := worker{ in: make(chan int), done: func() { wg.Done() }, } go doWork(id, w) return w } func chanDemo() { var wg sync.WaitGroup //并发 等待 var workers [10]worker for i := 0; i < 10; i++ { workers[i] = createWorker(i, &wg) //10个worker将 } wg.Add(20) //20个任务 for i, worker := range workers { worker.in <- 'a' + i } for i, worker := range workers { worker.in <- 'A' + i } wg.Wait() } func main() { chanDemo() }
3、使用Channel实现树的遍历
https://www.cnblogs.com/ycx95/p/9361122.htm
4、CSP模型实现
用select进行调度
例子1:拿不到数据不会报错,但是会进default,输出是:No value received
channel中如果会进入非阻塞,可以用select+default
func generator() chan int { out := make(chan int) go func() { i := 0 for { time.Sleep( time.Duration(rand.Intn(1500)) * time.Millisecond) out <- i i++ } }() return out } func worker(id int, c chan int) { for n := range c { time.Sleep(time.Second) fmt.Printf("Worker %d received %d\n", id, n) } } func createWorker(id int) chan<- int { c := make(chan int) go worker(id, c) return c } func main() { var c1, c2 = generator(), generator() var worker = createWorker(0) var values []int tm := time.After(10 * time.Second) tick := time.Tick(time.Second) for { var activeWorker chan<- int var activeValue int if len(values) > 0 { activeWorker = worker activeValue = values[0] } select { case n := <-c1: values = append(values, n) case n := <-c2: values = append(values, n) case activeWorker <- activeValue: values = values[1:] case <-time.After(800 * time.Millisecond): fmt.Println("timeout") case <-tick: fmt.Println( "queue len =", len(values)) case <-tm: fmt.Println("bye") return } } }
输出结果:
queue len = 3 Worker 0 received 0 queue len = 5 Worker 0 received 0 queue len = 5 Worker 0 received 1 queue len = 10 Worker 0 received 1 queue len = 10 Worker 0 received 2 queue len = 11 Worker 0 received 2 queue len = 12 Worker 0 received 3 queue len = 13 Worker 0 received 3 queue len = 14 Worker 0 received 4 bye Process finished with exit code 0
5、传统同步机制
这里只是一个示例,详情参见https://www.cnblogs.com/ycx95/p/9358739.html 的atomic.go
但是不建议。