golang channel 使用(1) 解决部分死锁和接收问题

1 从一个简单示例开始
package main

import "fmt"

func main() {
	//创建channel
	ch := make(chan string)
	//往channel发数据
	ch <- `data1`
	//打印
	fmt.Printf(<-ch)
}

执行程序报错
在这里插入图片描述
报错协程死锁
原因是只有往发数据方,却没有接收数据方。
在go语言中channel被定义为协程之间的交流通道因此需要两个协程1为发送1为接收
修改代码

package main

import "fmt"

func main() {
	//创建channel
	ch := make(chan string)
	//使用协程执行接收
	go func() {
		for  {
			//打印
			fmt.Printf(<-ch)
		}
	}()
	//往channel发数据
	ch <- `data1`
}

执行成功
结果展示
在这里插入图片描述

2 换一种channel创建方式
package main

import "fmt"

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 10 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//接收打印数据
	for   {
		fmt.Println(<-ch)
	}
}

依然执行程序报错
结果展示
在这里插入图片描述
还是协程死锁,但是数据都打印出来
两个知识点

  • 创建channel的方式有两种
    1 make(chan type)
    2 make(chan type , bufferSize)
    第一种创建了common channel
    第二种创建了buffer channel
    两种chan的区别在于buffer channel可以缓存部分数据也就是说在没有达到buffer channel最大size之前,哪怕没有接收方也可以正常执行。
    所以数据被正常的发送和打印了
  • 不是说可以正常执行吗为啥还报错
    仔细观察可以发现报错大体一样还是有区别的
    一个是 chan send 一个是 chan receive
    也就是发送和接收有一个卡住都会死锁
    修改代码
package main

import (
	"fmt"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 10 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//接收打印数据
	for i:= 0 ; i < 10 ; i++  {
		fmt.Println(<-ch)
	}
}

执行成功
结果展示
在这里插入图片描述

3 由2引发的问题

通过第二段的代码会产生一个问题,怎么肯定发送者一定发送了buffer size的数据哪?
代码示例

package main

import (
	"fmt"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 5 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//接收数据
	for i:= 0 ; i < 10 ; i++  {
		fmt.Println(<-ch)
	}
}

执行报错
结果展示
在这里插入图片描述
报错内容:chan receive 死锁
原因是我只往chan发了五个数据
到第六次循环执行接收chan的时候自然就死锁了
那么在不确定chan长度的时候怎么接收哪?
来段代码

package main

import (
	"fmt"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 5 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//获得channel长度
	l := len(ch)

	//接收数据
	for i:= 0 ; i < l ; i++ {
		fmt.Println(<-ch)
	}
}

可以先获得channel的长度

package main

import (
	"fmt"
	"time"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 5 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	timer := time.NewTicker(time.Microsecond)
	//接收数据
	for {
		select {
		case msg := <- ch:
			fmt.Println(msg)
		case  <-timer.C:
		}
	}
}

也可以使用select语法
都可以执行成功
结果展示
在这里插入图片描述
实际工作中第二种方式会经常用到
这种方式限定于只是暂时没数据的情况(暂时接收不到数据没关系过一会儿在试试)
还有一种情况数据全部发送完了不会再有数据了,这时怎么做那?
上代码

package main

import (
	"fmt"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 5 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//声明chan关闭
	close(ch)

	//接收数据
	for {
		n,ok := <- ch
		if !ok {
			break
		}
		fmt.Println(n)
	}
}

当发送方确定不会有数据后发送后,声明chan关闭了
接收时通过第二参数判断接收数据
另一种写法

package main

import (
	"fmt"
)

func main() {
	//创建channel
	ch := make(chan string , 10)

	//发送数据
	for i:= 0 ; i< 5 ; i++ {
		ch <- fmt.Sprintf("data%d" , i)
	}

	//声明chan关闭
	close(ch)

	//第二种接收数据写法
	for data := range ch {
		fmt.Println(data)
	}
}

结果是一样的
在这里插入图片描述
以上介绍了golang channel 的三个主要知识点
1 简单channel使用
2 缓存channel使用
3 关闭channel方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值