go routine channel select

一、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

你可能感兴趣的:(Golang语言)