iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中怎么读写Channel数据
  • 276
分享到

Golang中怎么读写Channel数据

2023-07-04 15:07:44 276人浏览 泡泡鱼
摘要

本文小编为大家详细介绍“golang中怎么读写Channel数据”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中怎么读写Channel数据”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Channel

本文小编为大家详细介绍“golang中怎么读写Channel数据”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中怎么读写Channel数据”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

    Channel 的一些实战说明

    关于 close Channel

    Golang中怎么读写Channel数据

    close Channel 的一些说明

    channel 不需要通过 close 来释放资源,这个是它与 Socket、file 等不一样的地方,对于 channel 而言,唯一需要 close 的就是我们想通过 close 触发 channel 读事件。

    • close chan 对 chan 阻塞无效,写了数据不读,直接 close,还是会阻塞的。

    • 如果 channel 已经被关闭,继续往它发送数据会导致 panic send on closed channel

    • closed 的 channel,再次关闭 close 会 panic

    • close channel 的推荐使用姿势是在发送方来执行,因为 channel 的关闭在接收端能感知到,但是发送端感知不到,因此一般只能在发送端主动关闭。而且大部分时候可以不执行 close,只需要读写即可。

    • 从一个已经 close 的 chan 中读取数据,是可以读取的,读到的数据为 0

    • 读取的 channel 如果被关闭,并不会影响正在读的数据,它会将所有数据读取完毕,在读取完已发送的数据后会返回元素类型的零值(zero value)。

    v, ok := <-ch 判断是否 close

    比如 v, ok := <-ch 中 ok 是一个 bool 类型,可以通过它来判断 channel 是否已经关闭,如果 channel 关闭该值为 false ,此时 v 接收到的是 channel 类型的零值。比如:channel 是传递的 int, 那么 v 就是 0 ;如果是结构体,那么 v 就是结构体内部对应字段的零值。

    _,ok := <-ch对应的函数是 func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool),入参block含义是当前goroutine是否可阻塞,当block为false代表的是select操作,不可阻塞当前goroutine的在channel操作,否则是普通操作(即_, ok不在select中)。返回值selected代表当前操作是否成功,主要为select服务,返回received代表是否从channel读到有效值。它有3种返回值情况:

    • block为false,即执行select时,如果channel为空,返回(false,false),代表select操作失败,没接收到值。

    • 否则,如果channel已经关闭,并且没有数据,ep即接收数据的变量设置为零值,返回(true,false),代表select操作成功,但channel已关闭,没读到有效值。

    • 否则,其他读到有效数据的情况,返回(true,ture)。

    优雅判断是否 close 的封装
    package mainimport "fmt"type T intfunc IsClosed(ch <-chan T) bool {select {case <-ch:return truedefault:}return false}func main() {c := make(chan T)fmt.Println(IsClosed(c)) // falseclose(c)fmt.Println(IsClosed(c)) // true}

    for-range 读取 Channel 数据

    不管是有缓冲还是无缓冲,都可以使用 for-range 从 channel 中读取数据,并且这个是一直循环读取的。

    for-range 中的 range 产生的迭代值为 Channel 中发送的值,如果已经这个 channel 已经 close 了,那么首先还会继续执行,直到所有值被读取完,然后才会跳出 for 循环,因此,通过 for-range 读取 chann 数据会比较方便,因为我们只需要读取数据就行了,不需管他的退出,在 close 之后如果数据读取完了会自动帮我们退出。如果既没有 close 也没有数据可读,那么就会阻塞到 range 这里,除非有数据产生或者 chan 被关闭了。但是如果 channel 是 nil,读取会被阻塞,也就是会一直阻塞在 range 位置。

    一个示例如下:

       ch := make(chan int)   // 一直循环读取 range 中的迭代值   for v := range ch {        // 得到了 v 这个 chann 中的值        fmt.Println("读取数据:",v)   }

    select 读写 Channel 数据

    • select 的 case 分支里面,可以读数据,也可以写数据。最多只允许有一个 default case,它可以放在 case 列表的任何位置,并且没有任何影响。

    • select 可以同时处理多个 channel,如果有同时多个 case 分支可以去处理,比如同时有多个 channel 可以接收数据,那么 Go 会伪随机(pseudo-random)的选择一个 case 处理。如果没有 case 需要处理,则会选择 default 分支去处理。如果没有 default case,则 select 语句会阻塞,直到某个 case 分支可以处理了。

    • 每次 select 语句的执行,是会扫描完所有的 case 后才确定如何执行,而不是说遇到合适的 case 就直接执行了。

    • 对于 nil channel 上的操作会一直被阻塞,如果没有 default case,只有 nil channel 的 select 会一直被阻塞。

    • select 语句和 switch 语句一样,它不是循环,它只会选择一个 case 来处理,如果想一直处理channel,你可以在外面加一个无限的 for 循环

    for {    select {    case c <- x:        x, y = y, x+y    case <-quit:        fmt.Println("quit")        return    }}

    Channel 的读写超时机制【select + timeout】

    我们的一般常见场景就是,当我们从 chann 中进行读取数据,或者写入数据的时候,想要快速返回得到是否成功的结果,如果被 chann 阻塞后,需要指定一定的超时时间,然后如果在超时时间内还没有返回,那么就超时退出,不能一直阻塞在读写 chann 的流程中。

    Go 的 time 库里面,提供了 time.NewTimer()、time.After()、time.NewTicker() 等方法,最终都可以通过这些方法来返回或者得到一个 channel,然后向这个 channel 中发送数据,就可以实现定时器的功能。

    channel 可以通过 select + timeout 来实现阻塞超时的使用姿势,超时读写的姿势如下:

    // 通过 select 实现读超时,如果读 chann 阻塞 timeout 的时间后就会返回func ReadWithSelect(ch chan int) (x int, err error) {timeout := time.NewTimer(time.Microsecond * 500)select {case x = <-ch:return x, nilcase <-timeout.C:return 0, errors.New("read time out")}}// 通过 select 实现写超时,如果写 chann 阻塞 timeout 的时间后就会返回func WriteChWithSelect(ch chan int) error {timeout := time.NewTimer(time.Microsecond * 500)select {case ch <- 1:return nilcase <-timeout.C:return errors.New("write time out")}}

    一个简单的实操代码示例

    package mainimport ("fmt""runtime""time")func DoWorker() {c := make(chan bool, 1)go func() {time.Sleep(100 * time.Millisecond) // 等待 100ms 后写入,和后面的读超时配合,看超时判断结果c <- true}()go func() {timeout := time.NewTimer(time.Millisecond * 105) // 设置 105 ms 超时,如果超时没有读取到则 timeoutselect {case x := <-c:fmt.Printf("read chann:%v\n", x)case <-timeout.C:fmt.Println("read timeout")}fmt.Printf("over select\n\n")}()}func main() {fmt.Printf("start main num:%v\n", runtime.NumGoroutine())go func() {for {time.Sleep(1 * time.Second)fmt.Printf("start go DoWorker\n")go DoWorker()}}()for {time.Sleep(4 * time.Second)fmt.Printf("now main num:%v\n", runtime.NumGoroutine())}}输出:start main num:1start go DoWorkerread chann:trueover selectstart go DoWorkerread chann:trueover selectstart go DoWorkerread chann:trueover select

    TryEnqueue 无阻塞写 Channel 数据

    有些场景,我们期望往缓冲队列中写入数据的时候,如果队列已满,那么不要进行写阻塞,而是写完发现队列已满就抛错,那么我们可以通过如下机制的封装来实现,原理是通过一个 select 和 一个 default 语句去实现,有一个 default 就不会阻塞了:

    var jobChan = make(chan int, 3)func TryEnqueue(job int) bool {select {case jobChan <- job:fmt.Printf("true\n")  // 队列未满return truedefault:fmt.Printf("false\n") // 队列已满return false}}

    Channel 常见错误和根因分析

    fatal error: all goroutines are asleep - deadlock 问题解决和优化

    参考 go-language-fatal-error-all-goroutines-are-asleep-deadlock,在 main 函数里面,如果要 通过 chann 等待其他子协程的往 chann 中写入数据,但是并没有其他子协程写入或者其他协程没有写入就提前退出或者结束了,此时,main goroutine 协程就会等一个永远不会来的数据,那整个程序就永远等下去了,这个时候就会报上述错误。

    fatal error: all goroutines are asleep - deadlock! 异常的示例,在 main 里面,往 chann 中写超过缓冲数量的数据,这个时候,main 是要期望能够从有其他协程读取这些数据的,但是 main 里面并没有,因此就会报错:

    package mainimport "fmt"func main() {     channel := make(chan string, 2)    fmt.Println("1")     channel <- "h2"     fmt.Println("2")     channel <- "w2"     fmt.Println("3")     channel <- "c3"    // 执行到这一步,直接报 error     fmt.Println("...")     msg1 := <-channel     fmt.Println(msg1) }

    优化处理:

    package mainimport "fmt"func main() {     channel := make(chan string, 2)    fmt.Println("1")     channel <- "h2"     fmt.Println("2")     channel <- "w2"    fmt.Println("3")     select {    case channel <- "c3":         fmt.Println("ok")     default:         fmt.Println("channel is full !")     }    fmt.Println("...")     msg1 := <-channel     fmt.Println(msg1) }

    读到这里,这篇“Golang中怎么读写Channel数据”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网GO频道。

    您可能感兴趣的文档:

    --结束END--

    本文标题: Golang中怎么读写Channel数据

    本文链接: https://www.lsjlt.com/news/346394.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Golang中怎么读写Channel数据
      本文小编为大家详细介绍“Golang中怎么读写Channel数据”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中怎么读写Channel数据”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。Channel...
      99+
      2023-07-04
    • 如何在golang中读写json数据
      这篇文章给大家介绍如何在golang中读写json数据,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。什么是golanggolang 是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,其语...
      99+
      2023-06-14
    • golang中channel怎么使用
      今天小编给大家分享一下golang中channel怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。什么是 channe...
      99+
      2023-06-29
    • Golang中channel的原理解读(推荐)
      数据结构 channel的数据结构在$GOROOT/src/runtime/chan.go文件下: type hchan struct { qcount uint ...
      99+
      2024-04-02
    • golang channel怎么关闭
      Golang是一种十分简洁高效的编程语言,支持并发编程。在Golang中,channel是一种非常重要的并发通信机制,它可以用于协调两个并发执行的任务之间的同步操作。但是在使用channel的过程中,我们也会遇到一些问题,尤其是当我们需要关...
      99+
      2023-05-14
    • 怎么在Golang中实现channel
      怎么在Golang中实现channel?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。什么是golanggolang 是Google开发的一种静态强类型、编译型、...
      99+
      2023-06-14
    • 怎么在Golang中监听多个channel
      本篇内容介绍了“怎么在Golang中监听多个channel”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!select 关键字我们可...
      99+
      2023-07-05
    • 怎么使用Golang并发读取文件数据并写入数据库
      本篇内容介绍了“怎么使用Golang并发读取文件数据并写入数据库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!项目结构data文件夹中包含数...
      99+
      2023-07-02
    • 怎么用golang编写数据库
      要使用Golang编写数据库,您可以按照以下步骤进行操作: 安装数据库驱动:首先,您需要安装适用于Golang的数据库驱动程序。G...
      99+
      2024-02-29
      golang 数据库
    • Golang中怎么处理文件读写操作
      在Golang中处理文件读写操作通常使用os包和io/ioutil包。下面是一些常见的文件读写操作示例: 读取文件内容: pac...
      99+
      2024-03-13
      Golang
    • SQLite5中怎么使用Python来读写数据库
      本篇内容介绍了“SQLite5中怎么使用Python来读写数据库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!SQLite支持多种编程语言的...
      99+
      2023-06-22
    • 怎么在java中二进制读写数据流
      本篇文章给大家分享的是有关怎么在java中二进制读写数据流,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java是什么Java是一门面向对象编程语言,可以编写桌面应用程序、We...
      99+
      2023-06-14
    • 怎么做数据库读写分离
      这篇文章主要讲解了“怎么做数据库读写分离”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么做数据库读写分离”吧!实现方式对于读写分离的使用,主要分为两种方式...
      99+
      2024-04-02
    • Python怎么读写JSON格式数据
      今天小编给大家分享一下Python怎么读写JSON格式数据的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。JSON格式数据简介...
      99+
      2023-07-05
    • Cassandra中怎么进行数据的读写操作
      在Cassandra中进行数据的读写操作可以通过CQL(Cassandra Query Language)语句来实现。下面是一些常用...
      99+
      2024-04-09
      Cassandra
    • 如何编写 Golang 文件读写函数?
      非常抱歉,由于您没有提供文章标题,我无法为您生成一篇高质量的文章。请您提供文章标题,我将尽快为您生成一篇优质的文章。...
      99+
      2024-05-15
    • MySQL-数据库读写分离(中)
      ♥️作者:小刘在C站 ♥️个人主页: 小刘主页  ♥️努力不一定有回报,但一定会有收获加油!一起努力,共赴美好人生! ♥️学习两年总结出的运维经验,以及思科模拟器全套网络实验教程。专栏:云计算技术 ♥️小刘私信可以随便问,只要会...
      99+
      2023-08-31
      数据库 mysql sql
    • javascript怎么读写本地sqlite数据库
      这篇“javascript怎么读写本地sqlite数据库”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“javascript怎...
      99+
      2023-07-05
    • Python数据读写之Python读写CSV文件
      目录1. 读取CSV文件 csv.reader()2. 写入CSV文件1. 读取CSV文件 csv.reader() 该方法的作用相当于就是通过 ',' 分割csv格...
      99+
      2024-04-02
    • SQLite3数据库读写
      //插入数据 #include "CppSQLite3.h" #include <io.h> CppSQLite3DB db; BOOL re = _access("config.db", 0);//判文件是否存在 if (re...
      99+
      2023-01-31
      数据库
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作