Go语言学习笔记十(通道channel)

通道 channel
go语言使用channel进行goroutine之间的数据传输,channel在go语言中是一种特殊的数据类型,在任何时候同时只能有一个goroutine访问通道进行发送和获取数据。

声明通道的类型
var ch chan int
ch表示通道变量:通道内的数据类型
int表示通道变量:保存通道的变量
chan的空值是nil,声明后需要配合make后才能使用
创建通道
ch1 := make(chan int) //创建一个整型类型的通道
ch2 := make(chan interface{}) //创建一个空接口类型的通道,可以存放任意格式
type Equip struct{}
ch3 := make(chan *Equip) //创建Equip指针类型的通道,存放Equip类型数据
向通道中发送数据的例子

ch := make(chan interface{})
//将0放入通道中
ch <- 0
//将字符串放入通道中
ch <- "hello world"

使用通道接受数据
通道接收数据同样使用<-符号,通道收发数据在不同的两个goroutine之间进行。
阻塞式接受数据
data := <-ch 该语句在执行时会发生阻塞,直到接收到数据并赋值给data变量。
非阻塞式接收数据
data, ok := <-ch (data表示接收到的数据,未接收到数据时,data表示通道类型的零值)
ok 表示是否接收到数据

接收任意数据,忽略接收的数据
阻塞接收数据后,忽略从通道返回的数据,格式为 <-ch
代码示例如下

package main
import "fmt"

func main(){
	//创建通道
	ch := make(chan int)
	//开启一个goroutine并发函数
	go func(){
		fmt.Println("Start goroutine")
		ch <- 0
		fmt.Println("exit goroutine")
	}()
	<-ch
	fmt.Println("all done")
}

以上代码示例中ch<-0表示接收任意数据目的是通知main的goroutine。<-ch表示忽略任意数据目的是等待匿名goroutine。
通道循环接收数据
通道循环接收代码格式如下
for data := range ch {
}
代码格式如下

package main
import ("fmt" "time")

func main(){
	ch := make(chan int)
	go func(){
		for i := 3; i >=0; i--{
			ch <- i
			//每次发送完等待一定时间
			time.Sleep(time.Second)
		}
	}()
	for data := range ch {
		fmt.Println(data)
		//接收到0时停止
		if data == 0{
			break
		}
	}
}

并发打印数据示例

package main
import ("fmt")
func Printer(c chan int) {
	for {
		//从通道中获取数据
		data := <-c
		if data == 0 {
			break
		}
		fmt.Printf("%d ",data)
	}
	//通知main已经循环结束了
	c <- 0
}
func main(){
	ch := make(chan int)
	//并发执行Printer
	go Printer(ch)
	//循环发送数据
	for i := 1; i <= 10; i++{
		ch <- i
	}
	//通知Printer()结束循环了
	ch <- 0	
	//等待Printer()结束
	<- ch

创建单向通道

单向通道的声明格式
var ch1 chan<- int
ch1是通道示例名,chan<- 表示只能发送数据的通道 int 表示通道接受的数据
var ch2 <-chan int 这里的<-chan表示只能收到数据通道

带缓冲的通道
为通道增加一个有限大小存储空间的形成带缓冲通道。带缓冲通道在发送时无需等待接收方接收即可完成发送过程,并且不会发生阻塞,只有当存储满的时候才会发生阻塞。

带缓冲的通道创建格式
var ch := make(chan int 3)
这里的ch表示通道的实例, chan 创建通道的关键字, int表示通道数据类型 3表示缓冲容量大小。
带缓冲通道代码示例

package main
import "fmt"

func main(){
	ch := make(chan int 3)
	fmt.Println(len(ch))
	ch <- 1
	ch <- 2
	ch <-3
	fmt.Println(len(ch))

以上代码展示通道ch输入数据前后缓冲的大小。

通道的多路复用–同时处理接收和发送多个通道的数据
多路复用指的是在一个信道上传输多路信号或数据流的过程和技术。
在Go语言中可以使用select关键字,同时响应多个通道的操作。select的每一个case都会对应通道的收发过程。当收发完成时就会触发case中响应的语句。
设置格式如下
select {
case 操作1:
响应语句1
case 操作2:
响应语句2
default:
响应语句3
}
select多路复用中可以接收的样式如下
接收任意数据 case <- ch:
接收变量 case d := <-ch:
发送数据 case ch <-100:

示例:模拟远程调用过程(RPC)
模拟客户端代码

package main
import "time"

//模拟RPC客户端的请求和接收消息封装
func RPCClient(ch chan string, req string) (string, error){
	//向服务器发送请求
	ch <- req
	//等待服务器返回
	select{
		case ack := <-ch
			return ack, nil//返回结果
		case <-time.After(time.Second):
			return "",errors.New("Time out")//超时

模拟服务器端代码

//模拟RPC服务器端接收客户端请求和回应
func RPCSever(ch chan string){
	for {
		//接收客户端请求
		data := <-ch
		//打印接收到的数据
		fmt.Println("server received", data)
		//向客户端反馈收到
		ch <- "roger"
	}
}

主程序代码

package main
import "fmt"
func main(){
	//创建一个无缓冲字符串通道
	ch := make(chan string)
	//并发执行服务器逻辑
	go RPCServer(ch)
	//客户端请求数据和接收数据
	recv, err := RPCClient(ch, "hi")
	if err != nil {
		//发生错误打印
		fmt.Println(err)
	} else {
		//正常接收到数据
		fmt.Println("client received", recv)
	}
}

你可能感兴趣的:(golang,学习,开发语言)