Golang通道的无阻塞读写的方法示例
无论是无缓冲通道,还是有缓冲通道,都存在阻塞的情况,但其实有些情况,我们并不想读数据或者写数据阻塞在那里,有1个唯一的解决办法,那就是使用select结构。
这篇文章会介绍,哪些情况会存在阻塞,以及如何使用select解决阻塞。
阻塞场景
阻塞场景共4个,有缓存和无缓冲各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
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
Golang通道的无阻塞读写的方法示例的更多相关文章
- Golang并发编程有缓冲通道和无缓冲通道(channel)
无缓冲通道 是指在接收前没有能力保存任何值得通道.这种类型的通道要求发送goroutine和接收goroutine同时准备好,才能完成发送和接收操作.如果两个goroutine没有同时准备好,通道会导 ...
- 探真无阻塞加载javascript脚本技术,我们会发现很多意想不到的秘密
下面的图片是我使用firefox和chrome浏览百度首页时候记录的http请求 下面是firefox: 下面是chrome: 在浏览百度首页前我都将浏览器的缓存全部清理掉,让这个场景最接近第一次访问 ...
- javascript性能优化:创建javascript无阻塞脚本
javaScript 在浏览器中的运行性能,在web2.0时代显得尤为重要,成千上万行javaScript代码无疑会成为性能杀手, 在较低版本的浏览器执行JavaScript代码的时候,由于浏览器只使 ...
- 通过分析iframe和无阻塞脚本关系能让我们更懂iframe
在我上篇文章里,我提到一种使用iframe完成无阻塞脚本加载的方式,因为我对iframe的偏见很大,所以上篇文章里我没有展开讨论这个问题. 文章发表后有位网友问了我这样一个问题,下面是他问题的原文,如 ...
- JS脚本文件的位置对页面加载性能影响以及无阻塞脚本(javascript)模式
JS的阻塞特性:当<script>出现的时候,页面必须等待脚本文件的加载.解析.执行完毕后才能继续进行页面的渲染.不管脚本文件是以内联形式还是外部引入的形式出现在<script> ...
- WPF MVVM模式下的无阻塞刷新探讨
很多时候我们需要做一个工作,在一个方法体里面,读取大数据绑定到UI界面,由于长时间的读取,读取独占了线程域,导致界面一直处于假死状态.例如,当应用程序开始读取Web资源时,读取的时效是由网络链路的速度 ...
- 在 tornado 中异步无阻塞的执行耗时任务
在 tornado 中异步无阻塞的执行耗时任务 在 linux 上 tornado 是基于 epoll 的事件驱动框架,在网络事件上是无阻塞的.但是因为 tornado 自身是单线程的,所以如果我们在 ...
- Web Worker无阻塞UI的牛逼技术,html5,可惜无法敢于UI
众所周知,JavaScript是单线程的,JS和UI更新共享同一个进程的部分原因是它们之间互访频繁,但由于共享同一个进程也就会造成js代码在运行的时候用户点击界面元素而没有任何响应这样的情况,这么糟糕 ...
- 无阻塞加载和defer、async
无阻塞加载 把js放在head里,浏览器是怎么去执行它的呢,是按顺序加载还是并行加载呢?在旧的浏览器下,都是按照先后顺序来加载的,这就保证了加载的js依赖不会发生问题.但是少部分新的浏览器已经开始允许 ...
随机推荐
- 【Leetcode】【Easy】Merge Two Sorted Lists .
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing t ...
- QT的编译原理
https://www.cnblogs.com/findumars/p/4948896.html
- Mysql学习---基础操作学习
1.1. 基本操作 数据库引擎 Inodb:支持事务[原子性操作,完成一些列操作后才算完成操作,否则rollback] MyISAM: 支持全文索引,强调了快速读取操作,主要用于高负载的select ...
- php解决高并发设想
1.我突然想到一个解决系统并发的一个方法, 当然不算太友好, 就是并发时候,首先加载系统负载量文件, 如果到达一个值,比如60%,就跳到404页面,或者输出稍后之类的这样 2.静态文件和图片存到cdn ...
- 模糊搜索框(H5),兼容安卓和ios(令人头大的ios输入法)
项目里要可以实现,按照模糊,于是从jq22网站找到一个代码,效果如图: 具体的html代码:(复制,需要引入jq相关的支持文件) <!DOCTYPE html PUBLIC "-//W ...
- NET对象的跨应用程序域
NET对象的跨应用程序域 转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDoma ...
- IntelliJ IDEA设置编码格式
IntelliJ IDEA设置编码格式为UTF-8
- 解决SpringMVC拦截器拦截静态资源的问题。
在使用SpringMVC进行开发的时候,遇到了以下代码不能执行的情况.而且我已经正确导入了JQuery框架. <script type="text/javascript"&g ...
- ResNet 残差网络训练数据
https://github.com/tornadomeet/ResNet 图片地址: data/trian/cifar10_cifar10.rec data/train/cifar10_val.re ...
- 【原创】多字节版本下MFC控件处理字符集的BUG
工程项目属性: 字符集:多字节 stdafx.h文件中添加: #pragma comment(linker,"/manifestdependency:\"type='win32' ...