go 信道

go 信道(channel)

信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。

信道的声明

信道在go中算是一种特殊类型,所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的。

chan T 表示 T 类型的信道。

信道的零值为 nil。信道的零值没有什么用,所以可以 map 和切片所做的那样,用 make 来定义信道。

func main() {
	var c chan interface{}    // 只定义不初始话  --> 零值为 <nil>
	var c1 chan interface{} = make(chan interface{})    // 使用make完成初始化
}

信道的使用

信道的使用很简单,如下所以可以通过信道发送和接收数据

data := <-a  // 读取信道 a 中的值
a <- data // 将data的值写入信道a

信道旁的箭头方向指定了是发送数据还是接收数据。

在第一行,箭头对于 a 来说是向外指的,因此我们读取了信道 a 的值,并把该值存储到变量 data

在第二行,箭头指向了 a,因此我们在把数据写入信道 a

发送与接收默认是阻塞的

发送与接收默认是阻塞的。这是什么意思?当把数据发送到信道时,程序控制会在发送数据的语句处发生阻塞,直到有其它 Go 协程从信道读取到数据,才会解除阻塞。与此类似,当读取信道的数据时,如果没有其它的协程把数据写入到这个信道,那么读取过程就会一直阻塞着。

信道的这种特性能够帮助 Go 协程之间进行高效的通信,不需要用到其他编程语言常见的显式锁或条件变量。

示例:

func main() {
	fmt.Println("程序开始")
    // var a chan int  信道只定义不初始化 使用会报错
	var a chan int = make(chan int)   
	go test(a)
    data := <- a  // 程序在这里阻塞,为取到值就一直等着,和python中队列(Queue)一样
	fmt.Println("接收到的值为:",data)
	fmt.Println("程序结束")
}

func test(c chan int)  {
	c <- 1
}
程序开始
接收到的值为: 1
程序结束

死锁

使用信道下述几种情况会发送死锁(deadlock)现象

1. 只定义不初始化,使用会直接报错
var a chan int  
2. 初始化后不放值直接取值
func main() {
    fmt.Println("程序开始")
    var a chan int = make(chan int)  
    data := <- a   // 程序运行到这里就会在这里停顿  不取到值 不会向下运行
    go test(a)
    fmt.Println("接收到的值为:",data)
    fmt.Println("程序结束")
}
3. 初始化后 只放值不取值

func main() {
	fmt.Println("程序开始")
	var a chan int = make(chan int)
	a <- 1
	fmt.Println("程序结束")
}

单向信道

上述讨论的都是双向信道,既能通过信道发送数据,又能接收数据。ishi也可以创建单向信道。这种信道只能发送或者接收数据

func main() {
   fmt.Println("程序开始")
   var a chan<- int = make(chan<- int)
   go test1(a)
   //fmt.Println("接收到的值为:",data)
   //fmt.Println(<-a)   // 报错 单向信道只能存不能取
   fmt.Println("程序结束")
}

// 唯收信道
func test2(c <-chan int)  {
	fmt.Println(<-c)
}

// 唯送信道
func test1(c chan<- int) {
	c <- 1
}

只不过一个不能读取数据的唯送信道究竟有什么意义呢?

这就需要用到信道转换(Channel Conversion)了。把一个双向信道转换成唯送信道或者唯收(Receive Only)信道都是行得通的,但是反过来就不行。

func main() {
	fmt.Println("程序开始")
	var a = make(chan int)
	go test1(a)
	fmt.Println(<-a)  // 1 发生了信道转换,双向信道转换成唯送信道
	fmt.Println("程序结束")
}

func test1(c chan<- int) {
	c <- 1
}

信道循环

数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。

当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。

v, ok := <- ch

如果信道成功接收信道发送的数据,那么ok的值为true。当ok等于false时,说明信道已经关闭。从关闭的信道读取到的值会是该信道类型的零值。

func main() {
	c := make(chan int)
	go test3(c)
    // 使用死循环
	for {
		v, ok := <-c // 信道取值,如果没有关闭,ok就是true,如果关闭了,ok就是false
		if ok == false {
			break
		}
        fmt.Println("取出的值:", v)
	}
    // 使用for range循环
    for v:=range c {  // 一旦信道关闭,for循环就结束了
        fmt.Println("取出的值:",v)
    }
}

func test3(c chan int) {
	for i := 0; i < 10; i++ {
		c <- i
	}
	close(c)  // 关闭信道
}

缓冲信道

在上文中,所描述的的主要是无缓冲信道,无缓冲信道的发送和接收过程是阻塞的。

我们还可以创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。

通过向 make 函数再传递一个表示容量的参数(指定缓冲的大小),可以创建缓冲信道。

func main() {
	c := make(chan int, 5)   // 创建一个容量为5的缓冲信道
	fmt.Println(cap(c))   // 5
	fmt.Println(len(c))   // 0 
}
// 长度:目前信道有几个值
// 容量:信道能够放多少----> 初始化的时候决定,不能扩容
// 无缓冲信道, 长度和容量默认都是0

缓冲信道,只有写入的值大于缓冲信道的容量时,无法取出值是,才会报死锁(deadlock)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go&Python

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值