17.Golang channel的基本定义及使用

概述

此篇文章介绍 channel 的用法

  • 无缓冲 channel
  • 缓冲 channel
  • channel的关闭特点
  • range channel
  • select channel

每一种,配上完整的代码及相应的测试结果,对关键的部分,配置上图及对应说明。

实践

无缓冲 channel

未分配空间的 channel 具有 阻塞的功能。交互的 goroutine 两都都会阻塞的效果。

无缓充的 channel
在这里插入图片描述
总结如下:

  • 第1步,两个 goroutine 都到达通道,但都没有开始执行发送或接收
  • 第2步,左侧的 goroutine 将手伸进了通道,模拟了向通道发送数据的行为。此时,这个 goroutine 会在通道中被锁住,直道交换完成。
  • 第3步,右侧 goroutine 将手放入通道,模拟了从通道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成
  • 第4步与第5步,进行交换。并最终,在第6步,两个 goroutine 都将手从通道里拿出来,模拟了被锁住的 goroutine 得到释放。

代码

package main

import "fmt"

func main() {
	// 定义一个 channel,并没有分配空间
	c := make(chan int)

	// 匿名函数
	go func() {
		defer fmt.Println("goroutine调用结束...")
		fmt.Println("goroutine 正在运行...")
		c <- 666
	}()

	num := <-c
	fmt.Println("num:=", num)
	fmt.Println("main goroutine 结束。。。")

}

结果

执行结果如下:
在这里插入图片描述

缓冲 channel

在这里插入图片描述

  • 第1步,右侧的 goroutine 正在从通道接收一个值
  • 第2步,右侧的这个 goroutine 独立 完成了 接收值 的动作,而左侧的 goroutine 正在发送一个新值至通道里
  • 第3步,左侧的 goroutine 还在向通道发送新值,而右侧的 goroutine 正在从通道接收另外一个值。这两个步骤里的操作既不是同步的,也不会相互阻塞。
  • 第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。

特点:当 channel 已经满,再向里面写数据,就会阻塞,当 channel 为空时,从里面取数据也会阻塞。

代码

package main

import (
	"fmt"
	"time"
)

func main() {

	// 带有缓冲的 channel
	c := make(chan int, 3)
	fmt.Println("len(c)= ", len(c), " ,cap(c)=", cap(c))

	go func() {
		defer fmt.Println("子goroutine执行结束...")

		for i := 0; i < 4; i++ {
			c <- i
			fmt.Println("子goroutine正在运行,发送的元素=", i, "len(c)= ", len(c), " ,cap(c)=", cap(c))
		}
	}()

	time.Sleep(2 * time.Second)

	for i := 0; i < 4; i++ {
		num := <-c
		fmt.Println("num=", num)
	}

	fmt.Println("main 结束...")
}

结果

执行结果如下:
在这里插入图片描述

channel的关闭特点

  • channel 不像文件一样需要经常关闭,只有确实没有任何发送数据了,或者想显式的结束 range 循环之类的,才去关闭 channel
  • 关闭 channel 后,无法向 channel 再发送数据(引发 panic 错误后导致接收立即返回零值)
  • 关闭 channel 后,可以继续从 channel 接收数据
  • 对于 nil channel ,无论收发都会被阻塞

代码

package main

import "fmt"

func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		// close可以关闭一个 channel
		close(c)
	}()

	for {
		// ok 如果为true表示channel没有关闭,如果为false表示channel已经关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}

	fmt.Println("main finished...")
}

结果

执行结果如下:
在这里插入图片描述

range代码

range 写法,完整代码如下

package main

import "fmt"

func main() {
	c := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		// close可以关闭一个 channel
		close(c)
	}()

	// 可以使用 range 来迭代不断操作 channel
	for data := range c {
		fmt.Println(data)
	}
}

结果

range-channel 测试结果如下
在这里插入图片描述

select channel

代码

package main

import "fmt"

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Println(<-c)
		}
		// close可以关闭一个 channel
		quit <- 0
	}()

	x, y := 1, 1
	for {
		select {
		case c <- x:
			// 如果 c 可写,则该 case 会进来
			x = y
			y = x + y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}

}

结果

在这里插入图片描述

结束

Golang channel的 基本定义及使用 至此结束,如有疑问,欢迎评论区留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

流月up

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

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

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

打赏作者

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

抵扣说明:

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

余额充值