无论是无缓冲通道,还是有缓冲通道,都存在阻塞的情况,但其实有些情况,我们并不想读数据或者写数据阻塞在那里,有1个唯一的解决办法,那就是使用select结构。

这篇文章会介绍,哪些情况会存在阻塞,以及如何使用select解决阻塞。

阻塞场景

阻塞场景共4个,有缓存和无缓冲各2个。

无缓冲通道的特点是,发送的数据需要被读取后,发送才会完成,它阻塞场景:

  1. 通道中无数据,但执行读通道。
  2. 通道中无数据,向通道写数据,但无协程读取。
// 场景1
func ReadNoDataFromNoBufCh() {
noBufCh := make(chan int)
<-noBufCh
fmt.Println("read from no buffer channel success")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 场景2
func WriteNoBufCh() {
ch := make(chan int)
ch <- 1
fmt.Println("write success no block")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}

注:示例代码中的Output注释代表函数的执行结果,每一个函数都由于阻塞在通道操作而无法继续向下执行,最后报了死锁错误。

有缓存通道的特点是,有缓存时可以向通道中写入数据后直接返回,缓存中有数据时可以从通道中读到数据直接返回,这时有缓存通道是不会阻塞的,它阻塞的场景是:

  1. 通道的缓存无数据,但执行读通道。
  2. 通道的缓存已经占满,向通道写数据,但无协程读。
// 场景1
func ReadNoDataFromBufCh() {
bufCh := make(chan int, 1)
<-bufCh
fmt.Println("read from no buffer channel success")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}
// 场景2
func WriteBufChButFull() {
ch := make(chan int, 1)
// make ch full
ch <- 100
ch <- 1
fmt.Println("write success no block")
// Output:
// fatal error: all goroutines are asleep - deadlock!
}

使用Select实现无阻塞读写

select是执行选择操作的一个结构,它里面有一组case语句,它会执行其中无阻塞的那一个,如果都阻塞了,那就等待其中一个不阻塞,进而继续执行,它有一个default语句,该语句是永远不会阻塞的,我们可以借助它实现无阻塞的操作。

下面示例代码是使用select修改后的无缓冲通道和有缓冲通道的读写,以下函数可以直接通过main函数调用,其中的Ouput的注释是运行结果,从结果能看出,在通道不可读或者不可写的时候,不再阻塞等待,而是直接返回。

// 无缓冲通道读
func ReadNoDataFromNoBufChWithSelect() {
bufCh := make(chan int)
if v, err := ReadWithSelect(bufCh); err != nil {
fmt.Println(err)
} else {
fmt.Printf("read: %d\n", v)
}
// Output:
// channel has no data
}
// 有缓冲通道读
func ReadNoDataFromBufChWithSelect() {
bufCh := make(chan int, 1)
if v, err := ReadWithSelect(bufCh); err != nil {
fmt.Println(err)
} else {
fmt.Printf("read: %d\n", v)
}
// Output:
// channel has no data
}
// select结构实现通道读
func ReadWithSelect(ch chan int) (x int, err error) {
select {
case x = <-ch:
return x, nil
default:
return 0, errors.New("channel has no data")
}
}
// 无缓冲通道写
func WriteNoBufChWithSelect() {
ch := make(chan int)
if err := WriteChWithSelect(ch); err != nil {
fmt.Println(err)
} else {
fmt.Println("write success")
}
// Output:
// channel blocked, can not write
}
// 有缓冲通道写
func WriteBufChButFullWithSelect() {
ch := make(chan int, 1)
// make ch full
ch <- 100
if err := WriteChWithSelect(ch); err != nil {
fmt.Println(err)
} else {
fmt.Println("write success")
}
// Output:
// channel blocked, can not write
}
// select结构实现通道写
func WriteChWithSelect(ch chan int) error {
select {
case ch <- 1:
return nil
default:
return errors.New("channel blocked, can not write")
}
}

使用Select+超时改善无阻塞读写

使用default实现的无阻塞通道阻塞有一个缺陷:当通道不可读或写的时候,会即可返回。实际场景,更多的需求是,我们希望,尝试读一会数据,或者尝试写一会数据,如果实在没法读写,再返回,程序继续做其它的事情。

使用定时器替代default可以解决这个问题。比如,我给通道读写数据的容忍时间是500ms,如果依然无法读写,就即刻返回,修改一下会是这样:

func ReadWithSelect(ch chan int) (x int, err error) {
timeout := time.NewTimer(time.Microsecond * 500)
select {
case x = <-ch:
return x, nil
case <-timeout.C:
return 0, errors.New("read time out")
}
}
func WriteChWithSelect(ch chan int) error {
timeout := time.NewTimer(time.Microsecond * 500)
select {
case ch <- 1:
return nil
case <-timeout.C:
return errors.New("write time out")
}
}

结果就会变成超时返回:

read time out
write time out
read time out
write time out

转载于:https://www.cnblogs.com/xxcn/p/9907400.html

Golang通道的无阻塞读写的方法示例相关推荐

  1. 用js的document.write输出的广告无阻塞加载的方法(转)

    一.广告代码分析 很多第三方地广告系统都是使用document.write来加载广告,如下面地一个javascript地广告链接. 代码如下: <script type=text/javascr ...

  2. 利用DuplicateHandle()无驱动的另类读写内存方法,理论上可以过游戏保护读写

    突发奇想出的原创思路,在理论上经过完善应该可以做到过游戏保护无驱动读写内存 首先看一下MSDN对这个函数的定义 BOOL DuplicateHandle(HANDLE hSourceProcessHa ...

  3. 用js的document write输出的广告无阻塞加载的方法

    一.广告代码分析 很多第三方的广告系统都是使用document.write来加载广告,如下面的一个javascript的广告链接. 代码如下: <script type="text/j ...

  4. Golang中各种永远阻塞的方法

    在Golang中各种永远阻塞的姿势 Go的运行时的当前设计,假定程序员自己负责检测何时终止一个goroutine以及何时终止该程序. 可以通过调用os.Exit或从main()函数的返回来以正常方式终 ...

  5. 超越最新无监督域自适应方法,研究人员提轻量CNN新架构OSNet

    作者 | Kaiyang Zhou, Xiatian Zhu, Yongxin Yang, Andrea Cavallaro, and Tao Xiang 译者 | TroyChang 编辑 | Ja ...

  6. WPF MVVM模式下的无阻塞刷新

    MVVM模式下的无阻塞刷新的两种方法: //传统模式下的无刷新调用(主线程开新线程,新线程又调用主线程来更新UI) //第1步先在线程内部计算出需要绑定的数据 //第2步然后再使用Invoke/Beg ...

  7. 局域网服务器文件夹隐藏,局域网服务器共享文件夹访问权限设置win7隐藏无权限共享文件夹方法.docx...

    局域网服务器共享文件夹访问权限设置win7隐藏无权限共享文件夹方法 ??局域网服务器共享文件夹访问权限设置.win7隐藏无权限共享文件夹的方法在局域网文件管理中,文件服务器共享文件的管理一直是当前网络 ...

  8. html5 js阻塞加载,js无阻塞加载和defer、async详解_白峰_前端开发者

    无阻塞加载 把 下载是异步的没问题,但是每个javascript执行的时候还是同步的,就是先出现的script标签一定是先执行,即使是并行下载它是最后一个下载完成的,除非标有defer的script标 ...

  9. 无阻塞实时检测键盘输入

    本人小白一枚,第一次写博客,个位大佬多多指教. 之前一直使用opencv,里面的waitkey函数对于实时检测键盘输入还是比较友好.但是最近只用c++开发之后发现不会怎么实现无阻塞读取键盘输入.查资料 ...

最新文章

  1. ADO.NET与ORM的比较(5):MyBatis实现CRUD
  2. oracle 2日dba 11g,Oracle 11gR2 注意revoke dba 会一并回收通过”quota“分配的表空间限额...
  3. 2020-09-17
  4. JavaScript事件的捕获阶段(Capture phase)
  5. php 置信区间 计算,深度学习篇-如何理解置信区间
  6. [原创]关于设置linux中vim 显示行号
  7. SAP License:药店ERP系统如何实施
  8. 9.6 awk(上);9.7 awk(下)
  9. 一台计算机重量,一台重量仅为1.85kg的游戏本 机械师F117毒药
  10. windows 10下解压.gz后缀文件
  11. 智慧安全:信息安全监控平台
  12. Python中index函数用法总结
  13. 【虫师--系列15】性能测试知多少---系统架构分析
  14. 新出炉彩色游戏——炸弹战争1.0版
  15. 今日头条 爬虫 java_Java爬虫实践:Jsoup+HttpUnit爬取今日头条、网易、搜狐、凤凰新闻...
  16. GitHub下载加速网站
  17. 关于com.lowagie.text包的报错问题
  18. python 换脸 github_如何用200行Python代码“换脸”
  19. 小米平板刷机shell怎么退_小米平板2如何一键解锁?刷机教程图解
  20. 在Vue中使用WebSocket+HighCharts+Canvas实现高性能的频谱瀑布图

热门文章

  1. 怎样格式化电脑_160G移动硬盘做PE启动盘 要怎样做
  2. python中全局变量和局部变量关键字_Python中全局变量和局部变量的理解与区别
  3. android popupwindow dialog区别,Android PopUpWindow使用详解
  4. mysql int 默认值 为ull_mysql的 约束 数据库设计 数据库 存储 触发器 mysql 权限问题...
  5. 生成html_HTML页面生成器:使用JavaScript和Node创建CLI
  6. windows下用elasticdump导入json数据到Elasticsearch中
  7. 不止面试题,笔记源码统统都有
  8. Android程序员面试必备的知识点,附答案
  9. 【408预推免复习】计算机组成原理之计算机的发展及应用
  10. 【django轻量级框架】在线视频教育系统设计与实现