1.channel简介

Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。

它的操作符是箭头 <- 。用来协程间传递数据。

ch <- v    // 发送值v到Channel ch中
v := <-ch  // 从Channel ch中接收数据,并将数据赋值给v
ch := make(chan int, 100)  //容量(capacity)代表Channel容纳的最多的元素的数量,代表Channel的缓存的大小。
如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞。一个nil channel不会通信。
close(ch)  //关闭channel

无缓冲阻塞场景:1.没有协程在写却读 2.没有协程在读却写

有缓冲阻塞场景:1.缓冲中无数据却读 2.缓冲已满却写

无缓冲的channel,不管是入还是出,都会阻塞,所以在同一个goroutine中,不能同时对同一个无缓冲channel进行入和出操作;

带缓冲的channel,在队列满之前,不会阻塞;队列满之后,依然会阻塞。

2.应用1:作为一个FIFO队列

package main
//用channel实现队列,查看是否原子性
import (
	"errors"
	"fmt"
	"sync"
)

func main(){
	ch := make(chan int, 20)
	fmt.Println(len(ch))
	fmt.Println(cap(ch))
    for i:=0; i<20; i++{
    	ch <- i+1
	}
	wg := sync.WaitGroup{}
	wg.Add(30)
	var err error
	for j:=0; j<30; j++{
		go get(&ch, &wg, &err)
	}
	wg.Wait()
}

func get(ch *chan int, wg *sync.WaitGroup, err *error){
	defer wg.Done()
	select {
	case x := <- *ch:   //如果可以读出,就读
		fmt.Printf("get number: %d, channel size: %d\n", x, len(*ch))
		return
	default:     //失败返回
	    fmt.Println("no number")
		*err = errors.New("channel has no data")
		return
	}
}

3.应用2:实现类似sync.WaitGroup的同步

package main

import (
	"fmt"
	"time"
)

func main() {
	//用sleep实现定时器
	fmt.Println(time.Now())
	time.Sleep(time.Second)
	fmt.Println(time.Now())
	//用timer实现定时器
	timer := time.NewTimer(time.Second)
	fmt.Println(<-timer.C)
	//用after实现定时器
	fmt.Println(<-time.After(time.Second))
	//周期定时
	tiker := time.NewTicker(time.Second)
	for i :=1; i<4; i++{
			fmt.Println(<-tiker.C)
	}
	fmt.Println("------------------")
	//定时完成的操作写在协程里
	ticker := time.NewTicker(time.Second * 3)
	ch := make(chan int)  //该channel完成同步,实现下面的协程执行完主线程才结束
	go func() {
		var x int
		for x < 10 {
			select {
			case <-ticker.C:
				x++
				fmt.Printf("%d\n", x)
				fmt.Println(time.Now())
				time.Sleep(time.Second * 1)  //case里的操作不要超过定时时间,不然会不符合预期
			}
		}
		ticker.Stop()
		ch <- 0
	}()
	<-ch
}   
//这里的channel 实现了一个 sync.WaitGroup的效果,主进程直接到<-ch,无拥塞channel,里面没东西,直接拿是会阻塞的,直到里面有东西
//也就是上面协程里的10次定时都结束,往channel写一个东西,就终止阻塞,使程序结束
// ch := make(chan int)  和   ch := make(chan int, 1) 理解:
// 有1的缓冲channel,一个协程写入拿到这个数,其他再拿都拿不到,配合select,拿不到就走,那个协程执行完操作之后,再往里写个数,其他协程就可以拿了。
//其实不管是有缓冲还是无缓冲,单纯用都会阻塞,无非是有一个buffer
//但是,加上select,都可以实现不拥塞,直接过。

4.应用3:搭配select实现锁效果

mutex实现的锁:拥塞的,一个获锁,其他需要一直等待,直到上一个操作释放,再执行操作

channel实现的锁:可以选择拥塞不拥塞,得不到可以直接放弃,也可以等待一段时间执行其他操作。

搭配select 实现的是io操作。

package main
//实现初始化一台实例的锁,不同appid都可以更新map
//实现channle的拥塞锁
import (
	"errors"
	"fmt"
	"sync"
	"time"
)

func main(){
	ch := make(chan int, 1)
	ch <- 1
	wg := sync.WaitGroup{}
	var err error
	wg.Add(10)
	for i:=0; i<10; i++{
		go get_blockf(&ch, &wg, &err)  //一直拥塞
	}
	wg.Wait()
	wg.Add(10)
	for i:=0; i<10; i++{
		go get_block(&ch, &wg, &err)  //拥塞2s
	}
	wg.Wait()
	wg.Add(10)
	for i:=0; i<10; i++{
		go get_noblock(&ch, &wg, &err)  //无拥塞
	}
	wg.Wait()
}

func get_blockf(ch *chan int, wg *sync.WaitGroup, err *error){
	defer wg.Done()
	select {
	case x := <- *ch:   //如果可以读出,就读
		fmt.Printf("Block get number: %d, channel size: %d\n", x, len(*ch))
		time.Sleep(time.Second*1)
		*ch <-x+1
	}
	return
}

func get_block(ch *chan int, wg *sync.WaitGroup, err *error){
	defer wg.Done()
	select {
	case x := <- *ch:   //如果可以读出,就读
		fmt.Printf("Block 2s get number: %d, channel size: %d\n", x, len(*ch))
		time.Sleep(time.Second*1)
		*ch <-x+1
	case <- time.After(time.Second *20):  //等待过程中一直尝试,尝试成功即从ch取数,超时还取不到执行下面操作
		fmt.Println("no number")
		*err = errors.New("channel has no data")
	}
	return
}

func get_noblock(ch *chan int, wg *sync.WaitGroup, err *error){
	defer wg.Done()
	select {
	case x := <- *ch:   //如果可以读出,就读
		fmt.Printf("Block 2s get number: %d, channel size: %d\n", x, len(*ch))
		time.Sleep(time.Second*1)
		*ch <-x+1
	default:       //等待过程中一直尝试,尝试成功即从ch取数,超时还取不到执行下面操作
		fmt.Println("no number")
		*err = errors.New("channel has no data")
	}
	return
}