channel 和select的小问题

pssgo · 2018-08-29 15:53:11 · 1415 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2018-08-29 15:53:11 的主题,其中的信息可能已经有所发展或是发生改变。

新人刚在学习golang,遇到channel的这个问题,goroutine中的select中的case 的ok 会变成false 那? 发送到channel的数据都是ok的啊,如果我把ch1 的容量去掉的话 就正常了

这是什么问题那?

image.png


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1415 次点击  
加入收藏 微博
8 回复  |  直到 2018-08-31 16:52:38
justay
justay · #1 · 7年之前
v,ok=<-chan

这里的ok是说c是否关闭,因为关闭之后从chan种读取数据会立即返回。

c:=make(chan int,2)
v,ok:=<-c  // 阻塞,等待从c中读取数据
// 关闭chan
close(c)
v,ok:=<-c //不会阻塞,立即返回,如果此次从chan中读取到了值,则ok为true,否则为false且v为零值
pssgo
pssgo · #2 · 7年之前

@justay 问题是我的channel 没有全部接收完毕啊, chan怎么会被关闭那? 这是完整代码

package main

import (
    "fmt"
    "os"
)

func main() {
    ch1,ch2 := make(chan int, 3), make(chan int)

    go func() {
        v, ok, s := 0, false, ""
        for {
            select {
            case v, ok = <- ch1:
                s = "a"
            case v, ok = <- ch2:
                s = "b"
            }
            if ok {
                fmt.Println("v&s",v, s)
            }else {
                os.Exit(1)
            }
        }
    }()

    for i:= 1; i < 4; i++ {
        select {
        case ch1 <- i:
        case ch2 <- i:
        }
    }

    fmt.Println("send over")

    close(ch1)
    close(ch2)

    fmt.Println("ch",ch1,ch2)
    select {} // 阻塞进程
}
lee_
lee_ · #3 · 7年之前

i存入的channel可能是ch1 ch2
存入的值可能是:
ch1 : 1
ch2 : 2、3、4
在goroutine里select对准备就绪channel进行随机读取
所以存在这样的读取可能 第1、2次都是读到的ch1,所以就打印出1,第二次读到ch1,因为已经关闭,直接退出

lee_
lee_ · #4 · 7年之前
lee_lee_ #3 回复

i存入的channel可能是ch1 **或** ch2 存入的值可能是: ch1 : 1 ch2 : 2、3、4 在goroutine里select对准备就绪channel进行随机读取 所以存在这样的读取可能 第1、2次都是读到的ch1,所以就打印出1,第二次读到ch1,因为已经关闭,直接退出

去掉缓存后,是同步读写,channel的两端有一端未准备好,另一端也不会执行相应的操作,类似于对i的输出进行了串行化处理,所以i会打印出1、2、3、4然后退出

justay
justay · #5 · 7年之前
pssgopssgo #2 回复

@justay 问题是我的channel 没有全部接收完毕啊, chan怎么会被关闭那? 这是完整代码 ```golang package main import ( "fmt" "os" ) func main() { ch1,ch2 := make(chan int, 3), make(chan int) go func() { v, ok, s := 0, false, "" for { select { case v, ok = <- ch1: s = "a" case v, ok = <- ch2: s = "b" } if ok { fmt.Println("v&s",v, s) }else { os.Exit(1) } } }() for i:= 1; i < 4; i++ { select { case ch1 <- i: case ch2 <- i: } } fmt.Println("send over") close(ch1) close(ch2) fmt.Println("ch",ch1,ch2) select {} // 阻塞进程 } ```

利用selectchan进行操作是随机选取chan的。无论是往里面写数据或是读数据。

    for i:= 1; i < 4; i++ {
        select {
        case ch1 <- i:
        case ch2 <- i:
        }
    }

这段会导致ch1,ch2里面的内容[1,2,3]是随机分布的。

            select {
            case v, ok = <- ch1:
                s = "a"
            case v, ok = <- ch2:
                s = "b"
            }

这段读的顺序也是随机的,随机从ch1,ch2里读取数据。想判断的话不要用同一个变量, 分别用不同的ok变量则可以避免你的问题.

fuhuizn
fuhuizn · #6 · 7年之前

你这么改一下:

select {
            case v1, ok1 = <- ch1:
                s = "a"
                v = v1
                ok = ok1
            case v1, ok1 = <- ch2:
                s = "b"
                v = v1
                ok = ok1
            }
fuhuizn
fuhuizn · #7 · 7年之前
fuhuiznfuhuizn #6 回复

你这么改一下: ``` select { case v1, ok1 = <- ch1: s = "a" v = v1 ok = ok1 case v1, ok1 = <- ch2: s = "b" v = v1 ok = ok1 } ```

case v1, ok1 = <- ch1: 等号钱加上冒号: case v1, ok1 := <- ch1:

pssgo
pssgo · #8 · 7年之前

@fuhuizn @justay @lee_ 谢谢各位我懂啦,因为随机发送到的chan不一样第一次正常,第二次可能某个通道已经关闭了 ok是false 所以才会退出, 谢谢各位

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传