我的编程空间,编程开发者的网络收藏夹
学习永远不晚

golangchannel读取数据的几种情况

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

golangchannel读取数据的几种情况

用var定义channel且不make

wg := sync.WaitGroup{}
var ch chan string

read := func() {
    fmt.Println("reading")
    s := <-ch
    fmt.Println("read:", s)
    wg.Done()
}

write := func() {
    fmt.Println("writing")
    s := "t"
    ch <- s
    fmt.Println("write:", s)
    wg.Done()
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()

输出:

waiting
writing
reading
fatal error: all goroutines are asleep - deadlock!

这种情况并不是报错空指针,而是死锁。加上make看看

用var定义channel且make

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    fmt.Println("reading")
    s := <-ch
    fmt.Println("read:", s)
    wg.Done()
}

write := func() {
    fmt.Println("writing")
    s := "t"
    ch <- s
    fmt.Println("write:", s)
    wg.Done()
}

wg.Add(2)
go read()
go write()

输出

waiting
writing
reading
read: t
write: t

这种情况没什么毛病,之所以先输出的read,是因为IO机制。下面给写加上for

直给写操作加for

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    fmt.Println("reading")
    s := <-ch
    fmt.Println("read:", s)
    wg.Done()
}

write := func() {
    for {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

waiting
reading
writing
write: t
writing
read: t
fatal error: all goroutines are asleep - deadlock!

报错说所有的协程都睡着,意思就是runtime发现没有能拿来调度的协程了,报错退出。如果是在大项目中,这里则会阻塞,runtime会调度其他可运行的协程。下面把for移到读操作上。

直给读操作加for

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    for {
        fmt.Println("reading")
        s := <-ch
        fmt.Println("read:", s)
    }
    wg.Done()
}

write := func() {
    fmt.Println("writing")
    s := "t"
    ch <- s
    fmt.Println("write:", s)
    wg.Done()
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

waiting
reading
writing
write: t
read: t
reading
fatal error: all goroutines are asleep - deadlock!

跟上面现象基本一样,不再赘述,然后给俩操作都加上for

读写都加for

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    for {
        fmt.Println("reading")
        s := <-ch
        fmt.Println("read:", s)
    }
    wg.Done()
}

write := func() {
    for {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

waiting
writing
reading
read: t
write: t
writing
reading
read: t
reading
write: t
writing
write: t
writing
...

结果当然就是死循环了,这个很好理解。接下来才是本文的重点:读数据的第二个参数。我们先保持其他的都不动,在读的时候接收第二个返回值。

读channel的第二个返回值

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    for {
        fmt.Println("reading")
        s, ok := <-ch
        fmt.Println("read:", s, ok)
    }
    wg.Done()
}

write := func() {
    for {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

waiting
writing
reading
read: t true
reading
write: t
writing
write: t
writing
read: t true
reading
read: t true
reading
write: t
...

可以看出来,这第二个返回值是个bool类型,目前全都是true。那么什么时候会是false呢,把channel关上试试。为了更直观,把字符串的长度一起输出

关闭channel继续读

wg := sync.WaitGroup{}
var ch = make(chan string)

read := func() {
    for {
        fmt.Println("reading")
        s, ok := <-ch
        fmt.Println("read:", len(s), s, ok)
    }
    wg.Done()
}

write := func() {
    for i := 0; i < 5; i++ {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
    close(ch)
}

wg.Add(2)
go read()
go write()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

waiting
writing
reading
read: 1 t true
reading
write: t
writing
write: t
writing
read: 1 t true
reading
read: 1 t true
reading
write: t
writing
write: t
writing
read: 1 t true
reading
read: 1 t true
reading
write: t
read: 0  false
reading
read: 0  false
reading
read: 0  false
...

接下来就是很规律的死循环了。这样是不是可以猜测,从已经close的channle读数据,会读到该数据类型的零值,且第二个返回值为false?再试试给channel加个buffer,先写完关上再开始读

写完然后关闭channel再开始读

wg := sync.WaitGroup{}
var ch = make(chan string, 5)

read := func() {
    for {
        fmt.Println("reading")
        s, ok := <-ch
        fmt.Println("read:", len(s), s, ok)
    }
    wg.Done()
}

write := func() {
    for i := 0; i < 5; i++ {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
    close(ch)
    fmt.Println("closed")
}

wg.Add(2)
write()
go read()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
closed
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 0  false
reading
read: 0  false
reading
read: 0  false
...

我们把写操作前的go关键字去了,并且在关闭channel之后加了log。可以很清晰的看到,先往channel里写了5次,然后close了,之后才有wait及read的log。并且前5个ok是true,后面循环输出false。现在我们可以得出结论当channel关闭且数据都读完了,再读数据会读到该数据类型的零值,且第二个返回值为false。下面再套上select

加个select

wg := sync.WaitGroup{}
var ch = make(chan string, 5)

read := func() {
    for {
        fmt.Println("reading")
        select {
        case s, ok := <-ch:
            fmt.Println("read:", len(s), s, ok)
        }

    }
    wg.Done()
}

write := func() {
    for i := 0; i < 5; i++ {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
    close(ch)
    fmt.Println("closed")
}

wg.Add(2)
write()
go read()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
closed
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 0  false
reading
read: 0  false
reading
read: 0  false
...

很明显跟上面现象一致,如果忘了关闭channel呢?

channel未及时关闭

wg := sync.WaitGroup{}
var ch = make(chan string, 5)

read := func() {
    for {
        fmt.Println("reading")
        select {
        case s, ok := <-ch:
            fmt.Println("read:", len(s), s, ok)
        }

    }
    wg.Done()
}

write := func() {
    for i := 0; i < 5; i++ {
        fmt.Println("writing")
        s := "t"
        ch <- s
        fmt.Println("write:", s)
    }
    wg.Done()
    //close(ch)
    //fmt.Println("closed")
}

wg.Add(2)
write()
go read()

fmt.Println("waiting")
wg.Wait()
fmt.Println("finish")

输出

writing
write: t
writing
write: t
writing
write: t
writing
write: t
writing
write: t
waiting
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
read: 1 t true
reading
fatal error: all goroutines are asleep - deadlock!

睡着了,然后报错。跟上面情况一样,如果是在大项目中,runtime会调度其他可运行的协程。最后来总结一下怎么操作才算优(sao)雅(qi)。

总结

  • 对写的一方来说,一定记着及时关闭channel,避免出现协程泄露。虽然它占得资源少,省点电不香么。
  • 对读的一方来说,除非十分确定数据的个数,最好是用for来读数据,省的在“管儿”里有“野数据”造成内存泄露。同时根据第二个返回值的真假来控制for循环,避免出现“无效工作量”

到此这篇关于golang channel读取数据的几种情况的文章就介绍到这了,更多相关golang channel读取内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

golangchannel读取数据的几种情况

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

下载Word文档

猜你喜欢

golangchannel读取数据的几种情况

本文主要介绍了golangchannel读取数据的几种情况,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-01

C++文件读取的4种情况汇总

前几天要用到C++读取文本文件,就学习了一下几种不同的读取方法,下面这篇文章主要给大家介绍了关于C++文件读取的4种情况,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
2023-01-11

Mysql数据库的导入导出方式(各种情况)

这篇文章主要介绍了Mysql数据库的导入导出方式(各种情况),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-03-08

PHP获取表单数据的方法有哪几种

PHP获取表单数据的方法有:1、使用预定义变量“$_GET”,可获取GET方法提交的数据;2、使用预定义变量“$_POST”,可获取POST方法提交的数据;3、使用预定义变量“$_REQUEST”,GET和POST方法提交的数据都可获取。
2017-02-24

数据库事务的应用场景:在各种情况下发挥其作用

数据库事务是一种确保数据库中的一组操作要么全部成功,要么全部失败的机制,它广泛应用于各种情况下,以保证数据的完整性和一致性。
数据库事务的应用场景:在各种情况下发挥其作用
2024-02-25

redis的三种部署方式和数据读取方式

redis redis的三种部署方式 1:主从复制,就是每台机器都是一样的,这种部署方式每台服务器都会向主服务器同步自己的数据,所以其中的数据内容是相同的 工作机制:从服务器连接到主服务器发送SYNC命令,主服务器接收命令,开始执行BGSAVEE命令申城R
redis的三种部署方式和数据读取方式
2017-03-18

php读取文件最后几行数据的代码是什么

在php中,可以利用file()和array_slice()函数来读取文件最后几行数据,实现代码“array_slice(file($file,FILE_IGNORE_NEW_LINES),-行数)”。
2018-11-25

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录