Go学习笔记—通道

Go学习笔记—通道


1、通道

channel是连接多个gotoutine的管道。可以从一个Go协程将值发送到管道,在别的Go协程中接收。

创建channel的语法是:

make(chan val-type)
  • make函数对于channel描述是:使用指定的缓冲区容量来初始化通道的缓冲区。

  • chan是关键字,代表channel

  • val-type是类型,代表通道接受什么类型的数据。

<-是通道操作符,指数据流的方向。使用channel<-来发送一个新的值到通道中。使用<-channel从通道中接收一个新的数据。

messages <- msg
//messages是通道名,将msg发送到通道messages中

msg <- messages
//用msg来接收从通道messages中传来的数据

定义一个通道来传输数据,用一个变量来接收传输的数据。

func main(){

	//定义一个channel通道
	messages := make(chan string)

	//将一个函数放在协程中运行,并用通道传输数据
	go func() {messages <- "ping"}()

	//接收通道传输的信息
	msg := <-messages
	fmt.Println(msg)
}

//ping

通过实例,可以看出消息“ping”,通过通道从一个Go协程发送到另一个中。

默认情况下,发送和接受操作在另一端准备好之前都会堵塞。

func sum(s []int,c chan int){
	sum := 0
	for _,v := range s{
		sum += v
	}
	c <- sum  //将和送入C
}

func main(){
	s := []int{7,2,4,5,8,1}

	c := make(chan int)
	go sum(s[:len(s)/2],c)
	go sum(s[len(s)/2:],c)

	x,y := <-c,<-c   //x,y从C中接受数据
	fmt.Println(x,y,x+y)
}

2、通道缓冲

在创建通道时,默认是无缓冲的。因此,只有在对应的接收通道准备好接收时,才允许进行发送。

可缓冲通道允许在没有对应接收方的情况下,缓存限定数量的值。

在通道有缓冲区时,没有对应的并发接收方也能保证通道的运行。

func main(){

	//定义一个channel,设置缓冲区为2
	messages := make(chan string,2)

	//通过goroutine传递信息
	//go func() {messages <- "ping"}()
	messages <- "buffered"
	messages <- "channel"

	//接收通道传输的信息
	msg := <-messages
	fmt.Println(msg)
    
    //直接输出通道接收的数据
	fmt.Println(<-messages)
}

//buffered
//channel

3、通道同步

使用通道来同步Go协程间的执行状态。

func worker(done chan bool){
	fmt.Println("working")
	//模拟阻塞
	time.Sleep(time.Second)
	fmt.Println("done")

	done <- true
}

这是一个将在Go协程中执行的函数,done通道将被用于通知其他Go协程这个函数已经工作完毕。发送true表示通道已经运行完毕。

func main(){
    done := make(chan bool,1)
    go worker(done)
    
    <-done
}

程序将在接收到通道中worker发出的通知前一直阻塞。

4、通道方向

当使用通道作为函数参数时,可以指定这个通道是不是只用来发送或者接收值。

使用通道作为参数,可以根据数据流的方向,只允许发送chan<-或者接收<-chan作为参数。

定义一个ping()函数,参数是只允许发送数据的通道。

func ping(pings chan<- string,msg string){
    pings <- msg
}

定义一个pong()函数,参数可以用来接收数据,也可以用来发送数据。

func pong(pings <-chan string,pongs chan<- string){
    msg := <-pings
    pongs <- msg
}

经过主函数的调用,将输出数据经过ping()函数,传输到通道pings中,由于ping()函数不能用来接收数据,但是输出数据还存在通道中,那么调用pong()函数,获取通道pings中的数据,再传到通道pongs中,再输出。

func main(){
	pings := make(chan string,1)
	pongs := make(chan string,1)
	//调用ping函数发送数据到msg中
	ping(pings,"passed message")
	//调用pong函数
	pong(pings,pongs)
	fmt.Println(<-pongs)
}

//passed message

5、通道选择器

Go语言中的通道选择器可以同时等待多个通道操作。

通道选择器select的语法为:

select{
    case handle1 :
    	statement
    case handle2 :
    	statement
}

//handle1和handle2是在case中做的处理或者判断
//statement是在符合条件后需要执行何种语句

定义两个通道,将通道中的值输出。

func main(){
	c1 := make(chan string)
	c2 := make(chan string)

	go func(){
        //通过时间来模拟阻塞
		time.Sleep(time.Second * 4)
		c1 <- "one"
	}()

	go func() {
        //通过时间来模拟阻塞
		time.Sleep(time.Second * 1)
		c2 <- "two"
	}()

	for i:=0;i<2;i++{
		//选择通道的运行
        select {
		case msg1 := <-c1:
			fmt.Println("received",msg1)
		case msg2 := <-c2:
			fmt.Println("received",msg2)
		}
	}
}

//received two
//received one

通过例子可以看出,选择器在并行的协程中,会选择优先解除阻塞的协程运行。

你可能感兴趣的:(认真学Go,go,golang)