文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

golangchannel读取数据的几种情况

2023-02-01 12:01

关注

用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)。

总结

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

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯