一、go routine channel
package main
import (
"fmt"
"time"
)
func worker(id int, c chan int) {
for n := range c {//读取channel
fmt.Printf("Worker %d received %c\n",
id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)//routine,可以理解为新开线程
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++ {
channels[i] <- 'a' + i//向channel中写入数据
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Second)//主线程也是个runtine
}
func bufferedChannel() {
c := make(chan int, 3)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'//如果没有人接收,可以在缓冲区存放3个,发送第四个时候会阻塞;如果make(chan int),如果没有人接收,在发送第二个时候,就会阻塞。
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)//关闭channel后不能发送和接收数据了
time.Sleep(time.Second * 20)
}
func main() {
fmt.Println("Channel as first-class citizen")
chanDemo()
//fmt.Println("Buffered channel")
//bufferedChannel()
//fmt.Println("Channel close and range")
//channelClose()
}
输出:
Worker 0 received a
Worker 1 received b
Worker 2 received c
Worker 3 received d
Worker 4 received e
Worker 4 received E
Worker 0 received A
Worker 7 received h
Worker 2 received C
Worker 9 received j
Worker 1 received B
Worker 6 received g
Worker 3 received D
Worker 8 received i
Worker 5 received f
Worker 5 received F
Worker 8 received I
Worker 6 received G
Worker 7 received H
Worker 9 received J
go runtine可以理解为新开个线程,不过实际上很多runtime可以运行在一个线程中,由go虚拟机去调度。
go channel是为了不同go runtine间通信而设置的。
1、发送channel后,只能在其他runtine中接收到channel后,才会继续执行下条语句,否则在发送channel时睡眠等待
2、如果是接收channel,如果没有发送者,则会一直等待,直到发送channel。
package main
import (
"fmt"
"time"
)
func worker(id int, c chan int) {
time.Sleep(time.Second * 5)
for n := range c {
fmt.Printf("Worker %d received %c\n",
id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func channelClose() {
c := make(chan int)
go worker(0, c)
c <- 'a'//发送channel后,只能在其他runtine中接收到channel后,才会继续执行下条语句,否则在发送channel时睡眠等待
fmt.Println("run here")
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Second * 20)
}
func main() {
fmt.Println("Channel close and range")
channelClose()
}
先睡眠等待5秒后,才打印run here。
二、channel完整例子
package main
import (
"fmt"
)
func doWork(id int,
w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c\n",
id, n)
w.done <- true
}
}
type worker struct {
in chan int
done chan bool
}
func createWorker(
id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
go doWork(id, w)
return w
}
func chanDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i)
}
for i, worker := range workers {
worker.in <- 'a' + i
}
for _, worker := range workers {
<-worker.done
}
for i, worker := range workers {
worker.in <- 'A' + i
}
for _, worker := range workers {
<-worker.done
}
}
func main() {
chanDemo()
}
输出:
Worker 9 received j
Worker 8 received i
Worker 7 received h
Worker 0 received a
Worker 2 received c
Worker 1 received b
Worker 3 received d
Worker 5 received f
Worker 6 received g
Worker 4 received e
Worker 1 received B
Worker 0 received A
Worker 3 received D
Worker 2 received C
Worker 4 received E
Worker 5 received F
Worker 9 received J
Worker 8 received I
Worker 6 received G
Worker 7 received H
package main
import (
"fmt"
)
func doWork(id int,
w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c\n",
id, n)
w.done <- true//没有人接收,会卡在这句上,而马上又要发送channel,接收的语句还在发送channel后面,死锁
}
}
type worker struct {
in chan int
done chan bool
}
func createWorker(
id int) worker {
w := worker{
in: make(chan int),
done: make(chan bool),
}
go doWork(id, w)
return w
}
func chanDemo() {
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i)
}
for i, worker := range workers {
worker.in <- 'a' + i
}
for i, worker := range workers {
worker.in <- 'A' + i
}
for _, worker := range workers {
<-worker.done
<-worker.done
}
}
func main() {
chanDemo()
}
输出:
Worker 8 received i
Worker 9 received j
Worker 1 received b
Worker 3 received d
Worker 5 received f
Worker 4 received e
Worker 6 received g
Worker 2 received c
Worker 0 received a
fatal error: all goroutines are asleep - deadlock!
三、select
package main
import (
"fmt"
"math/rand"
"time"
)
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 main() {
var c1 ,c2 = generator(), generator()
var c3 chan int
c3 = make(chan int)
for {
select {
case n := <-c1:
fmt.Println("Received from c1:", n)
case n := <-c2:
fmt.Println("Received from c2:", n)
case c3 <- 1:
fmt.Println("send c3:")
}
}
}
select和不加select有什么区别呢?
不加select:
如果接收时,没有人发送,会睡眠等待,如n := <-c1。如果发送,没有人接收,会睡眠等待如c3 <- 1。
加select:
如果接收时,没有人发送,会继续向下匹配,如n := <-c1。如果发送,没有人接收,会继续向下匹配如c3 <- 1。
如果全部没有匹配,会睡眠等待,直到有匹配项过来。如果有default,则走default,不会睡眠等待。
如果接收时,有人发送,匹配成功,进入case内语句执行后,继续下一轮匹配,如n := <-c1。
如果发送,有人接收,匹配成功,进入case内语句执行后,会继续向下匹配如c3 <- 1。
package main
import (
"fmt"
"math/rand"
"time"
)
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)
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)
fmt.Println("set values:", values)
case n := <-c2:
values = append(values, n)
fmt.Println("set values:", values)
case activeWorker <- activeValue:
values = values[1:]
fmt.Println("get values:", values)
case <-tm:
fmt.Println("bye")
return
}
}
}
输出:
//刚开始没有c1和c2没有接受到数据时,由于activeWorker是nil chan,所以此时会睡眠,但在select的时候直接跳过。如果所有case都不匹配,那么就睡眠了。
第一轮:---睡眠等待---
之后每次匹配所有项,如果c1和c2有人写,activeWorker有值并且读端正在等待读,则执行case;如果都不匹配,那么就睡眠等待直到匹配,再执行下一轮循环把数据拿出来。
第二轮:set values: [0] //从c1获取数据
第三轮:get values: [] //activeWorker有值,向其中写入数据,并且读端正在等待读,此时可写入数据,执行case语句
第四轮:---睡眠等待---
第五轮:set values: [0] //从c2获取数据
第六轮:set values: [0 1]
第七轮:---睡眠等待---
第八轮:set values: [0 1 1]
第九轮:---睡眠等待---
第十轮:set values: [0 1 1 2]
第十一轮:---睡眠等待---
Worker 0 received 0 //读端结束sleep,继续等待读状态,for n := range c {
第十二轮get values: [1 1 2] //activeWorker有值,向其中写入数据,并且读端正在等待读,此时可写入数据,执行case语句
set values: [1 1 2 2]
set values: [1 1 2 2 3]
Worker 0 received 0
get values: [1 2 2 3]
set values: [1 2 2 3 3]
set values: [1 2 2 3 3 4]
set values: [1 2 2 3 3 4 5]
Worker 0 received 1
get values: [2 2 3 3 4 5]
set values: [2 2 3 3 4 5 4]
set values: [2 2 3 3 4 5 4 5]
set values: [2 2 3 3 4 5 4 5 6]
set values: [2 2 3 3 4 5 4 5 6 6]
Worker 0 received 1
get values: [2 3 3 4 5 4 5 6 6]
set values: [2 3 3 4 5 4 5 6 6 7]
set values: [2 3 3 4 5 4 5 6 6 7 7]
Worker 0 received 2
get values: [3 3 4 5 4 5 6 6 7 7]
set values: [3 3 4 5 4 5 6 6 7 7 8]
set values: [3 3 4 5 4 5 6 6 7 7 8 9]
Worker 0 received 2
get values: [3 4 5 4 5 6 6 7 7 8 9]
set values: [3 4 5 4 5 6 6 7 7 8 9 8]
Worker 0 received 3
get values: [4 5 4 5 6 6 7 7 8 9 8]
set values: [4 5 4 5 6 6 7 7 8 9 8 10]
set values: [4 5 4 5 6 6 7 7 8 9 8 10 9]
Worker 0 received 3
get values: [5 4 5 6 6 7 7 8 9 8 10 9]
set values: [5 4 5 6 6 7 7 8 9 8 10 9 11]
set values: [5 4 5 6 6 7 7 8 9 8 10 9 11 10]
Worker 0 received 4
get values: [4 5 6 6 7 7 8 9 8 10 9 11 10]
set values: [4 5 6 6 7 7 8 9 8 10 9 11 10 11]
set values: [4 5 6 6 7 7 8 9 8 10 9 11 10 11 12]
bye