10、go语言:Channel

本文深入探讨了Go语言中的goroutine和channel机制,通过多个示例讲解了如何使用channel进行数据传递,包括channel作为参数、返回值及在结构体中的应用。文章还介绍了channel在任务协调、遍历树结构和调度中的作用,以及select语句的非阻塞式获取和定时检查功能。
摘要由CSDN通过智能技术生成

1、channel:

goroutine和goroutine的双向通道就是channel

func chanDemo(){
	//var c chan int //c==nil,用var定义的chan,等于nil的channel用不了
	c := make(chan int) //通常使用make来创建channel,可以直接使用
	c <- 1 //会死锁,发了数据没人收
	c <- 2
	n := <-c
	fmt.Println(n)
}

-----------------------------------------------------------------------
func chanDemo(){
	//var c chan int //c==nil,用var定义的chan,等于nil的channel用不了
	c := make(chan int) //通常使用make来创建channel,可以直接使用
	
	go func(){
		for { //开一个goroutine不断的在收
			n := <-c
			fmt.Println(n)
		}
	}()
	c <- 1 //会死锁,发了数据没人收
	c <- 2
	time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}

-------------------------提取----------------------------------------------
演示channel作为参数、以及数组

func worker(id int,c chan int){
	for { //开一个goroutine不断的在收
		n := <-c
		fmt.Printf("Worker %d received %d\n",id,n)
	}
}

func chanDemo(){
	var channels [10]chan int //建立channel的数组
	for i:=0;i<10;i++{
		channels[i] := make(chan int) //通常使用make来创建channel,可以直接使用
		go worker(i,channels[i] )
	}

	for i:=0;i<10;i++{
		channels[i] <- 'a'+ i
	}
	
	for i:=0;i<10;i++{
		channels[i] <- 'A'+ i
	}
	
	time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}

-------------------------演示channel作为返回值----------------------------------------------

//chan是用来送数据的
func createWorker(id int) chan<- int {
	c := make(chan int)
	go func() {
			for { //开一个goroutine不断的在收
				n := <-c
				fmt.Printf("Worker %d received %d\n",id,n)
			}
		}()
	return c
}

//<-chan收数据

func chanDemo(){
	var channels [10]chan<- int //建立channel的数组
	for i:=0;i<10;i++{
	channels[i] := createWorker(i) //通常使用make来创建channel,可以直接使用
	}
	
	for i:=0;i<10;i++{
	channels[i] <- 'a'+ i
	}
	
	for i:=0;i<10;i++{
	channels[i] <- 'A'+ i
	}
	
	time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}

------------------bufferedChannel------------------------------

func bufferedChannel(){
	c := make(chan int,3) //缓冲区
	go worker(0,c)
	c <- 1
	c <- 2
	c <- 3
	time.Sleep(time.Millisecond)
}

//将worker提取出来
func worker(id int,c chan int) {
	for { //开一个goroutine不断的在收
		n,ok := <-c //如果没有值,退出循环
		if !ok {
			break
		}
		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
}

//告诉别人数据发完了,发送方close
func channelClose(){
	c := make(chan int)
	go worker(0,c)
	c <- 1
	c <- 2
	c <- 3
	close(c)
	time.Sleep(time.Millisecond)
}

//接收方有两种方法处理close的channel
1、用ok标示;
2、用range

//或者用range获取channel的值

func worker(id int,c chan int) {
	for n := range c {
		fmt.Printf("Worker %d received %d\n",id,n)
	}
}

理论基础:Communication Sequential Process(CSP)
不要通过共享内存来通信;通过通信来共享内存

2、使用channel等待任务结束:

代码改动有点大

func doWork(id int,c chan int,done chan bool) {
	for n := range c {
		fmt.Printf("Worker %d received %d\n",id,n)
		//done <- true //直接这样,容易造成阻塞,等两次,因为一直发,需要有人收才行,简单的方法是再开一个协程
		go func(){ done <- true }() //并行地发done
	}
}

//建立一个结构
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.in,w.done)
	return w
}

func chanDemo(){
	var workers [10]worker //之前是channel的数组
	for i:=0;i<10;i++{
		workers[i] := createWorker(i) //通常使用make来创建channel,可以直接使用
	}
	
	for i,worker := range workers{
		worker.in <- 'a'+ i
		//<-worker.done
	}
	
	for  i,worker := range workers{
		worker.in <- 'A'+ i
		//<-worker.done
	}
	
	//wait for all of them
	for _,worker := range workers {
		<-worker.done
		<-worker.done
	}
	
	//workers[i]done的引入可以删掉sleep
	//time.Sleep(time.Millisecond)//不加这句话只会输出1,因为在输出2之前,main函数已经结束了
}

------------用waitGroup的方式替换done的方法------------------------------

func doWork(id int,c chan int,wg *sync.WaitGroup) {
	for n := range c {
		fmt.Printf("Worker %d received %d\n",id,n)
		wg.Done()
	}
}
func createWorker(id int,wg *sync.WaitGroup) worker {
	w := worker{
		in := make(chan int),
		wg: wg,
	}
	go doWork(id,w.in,wg)
	return w
}
//建立一个结构
type worker struct{
	in chan int
	wg *sync.WaitGroup //使用指针
}
func chanDemo(){
	var wg sync.WaitGroup
	var workers [10]worker //之前是channel的数组
	for i:=0;i<10;i++{
		workers[i] := createWorker(i,&wg) //通常使用make来创建channel,可以直接使用
	}
	wg.Add(20)
	for i,worker := range workers{
		worker.in <- 'a'+ i
		//<-worker.done
	}
	
	for  i,worker := range workers{
		worker.in <- 'A'+ i
		//<-worker.done
	}
	wg.Wait()
}

-----------------------再次包装Done的方法---------------------------------

func doWork(id int,w worker) {
	for n := range w.in {
		fmt.Printf("Worker %d received %d\n",id,n)
		w.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
}

3、使用channel进行任务的遍历:

实现树的遍历,第4章节有关于树的遍历相关代码

func (node *Node) TraverseWithChannel() chan *Node {
	out := make(chan *Node)
	go func(){
		node.TraverseFunc(func(node *Node){
			out <- node
			})
		close(out)
		}()
}

func main(){
	//省略结点的创建
	c := root.TraverseWithChanel()
	maxNode := 0
	//统计最大的结点数字
	for  node := range c {
		if node.Value > maxNode {
			maxNode = node.Value
		}
	}
}

4、用select进行调度:

func main(){
	var c1,c2 chan int
	//c1和c2同时收,谁来的快就优先处理谁,就用select
	//非阻塞式获取
	select {
		case n := <-c1:
			fmt.Printf("Received from c1:",n)
		case n := <-c2:
			fmt.Printf("Received from c2:",n)
		default:
			fmt.Printf("No value received")
	}
}
------------------------------------------------
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++
			}
		}()
}
func main(){
	var c1,c2 chan int
	//w := createWorker(0)
	// var w chan int //nil channel在select中是可以运行的,但是不会被select到
	var worker = createWorker(0) /利用nil channel 的性质
	//c1和c2同时收,谁来的快就优先处理谁,就用select
	n :=0
	hasValue := false
	for {
		var activeWorker chan<- int
		if hasValue {
			activeWorker = worker
		}
		//非阻塞式获取
		select {
			case n := <-c1:
				hasValue = true
				//w <- n
			case n = <-c2:
				hasValue = true
				//w <- n
			case activeWorker <-n:
				hasValue = false
				//case w <- n :
				//if hasValue {
				//send to w
				//}
		}
	}
}
//如果消耗速度太慢,上述n会被覆盖掉

func main(){
	var c1,c2 chan int
	var worker = createWorker(0) //利用nil channel 的性质
	var values []int //存起来
	//10秒后结束
	tm := time.After(10*time.Second)
	for {
		var activeWorker chan<- int
		var activeValue int
		if len(values) > 0 {
			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): //每次select花费的时间
				fmt.Println("timeout")
			case <-tm:
				fmt.Println("bye")
				return
		}
	}
}

----------------定时查看----------------------

func main(){
	var c1,c2 chan int
	var worker = createWorker(0) //利用nil channel 的性质
	var values []int //存起来
	//10秒后结束
	tm := time.After(10*time.Second)
	tick := time.Tick(time.Second)
	for {
		var activeWorker chan<- int
		var activeValue int
		if len(values) > 0 {
			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): //每次select花费的时间
				fmt.Println("timeout")
			case <-tick: //定时器
				fmt.Println("queue len =",len(values))
			case <-tm: //总时间确定程序运行时长
				fmt.Println("bye")
				return
		}
	}
}

select的使用:用default就不会被阻塞
定时器的使用
在select中使用nil channel

5、传统同步机制:

上节是通过channela进行通信,没有使用到锁
传统同步机制:
1、WaitGroup
2、Mutex
3、Cond
传统的同步机制是共享内存来进行通信

//原子,会引发冲突
//type atomicInt int

//改一下结构,包含锁以解除竞争
type atomicInt struct {
	value int
	lock sync.Mutex
}

func (a *atomicInt) increment(){
	a.lock.Lock()
	defer a.lock.Unlock()
	a.value++
}

func (a *atomicInt) get() int{
	a.lock.Lock()
	defer a.lock.Unlock()
	//return a.value
}

func main(){
	var a atomicInt
	a.increment()
	go func(){
		a.increment()
	}()
	time.Sleep(time.Millisecond)
	fmt.Println(a.get())
}

用命令行go run -race atomic.go 会提示冲突

//需要对加锁的数据
func (a *atomicInt) increment(){
	//只有在匿名函数中,defer才会起作用,defer控制在函数体里面
	func(){
		a.lock.Lock()
		defer a.lock.Unlock()
		a.value++
	}()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值