Go语言的timeout和select随机问题

663280439 · 2015-03-22 03:30:12 · 4774 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2015-03-22 03:30:12 的主题,其中的信息可能已经有所发展或是发生改变。

看无闻的Go教程中提到,Go的select对channel的操作,是随机的。我测试的时候,在select中对channel写数据,确实是随机的。但是,我还想证实一下,select对channel的读,也是随机的。下面是一个例子,我通过goroutine,对ch1和ch2两个channel读取数据。为了避免ch1和ch2刚顺序写完就被读出来,影响实验的准确性。我特意在goroutine中使用了timeout。但是我发现两个问题:

①:最终还是先输出ccc,再输出ddd,运行多少次都是一样。我的理解,虽然goroutine后台去执行了,但是休眠5s的过程中,ch1和ch2都已经有值了,那么休眠结束后,理应会随机先输出ccc或者ddd啊

②:关于timeout,我用sublime的插件gosublime开发,发现运行开始到结束,都是一下子输出的,没有体现出来时间停顿一个一个输出这个现象,但是,在Wide(http://studygolang.com/wide/playground )里面运行下面的代码,就是一个一个输出的,并非一下子都输出出来,这又是为什么?

package main

import "fmt"

import "time"

func main() {

    //定义两个字符串类型的channel
    ch1, ch2 := make(chan string, 0), make(chan string, 0)

    //启用goruntine不断从两个channel中读取数据
    go func() {
        fmt.Println("start")
        time.Sleep(5 * time.Second)
        fmt.Println("wait")
        time.Sleep(2 * time.Second)
        //i := 1
        for {
            //  fmt.Println(i)
            //  i++
            select {
            case v, ok := <-ch1:
                if ok {
                    fmt.Println(ok, v)
                } else {
                    fmt.Println("!!!!")
                }
            case v, ok := <-ch2:
                if ok {
                    fmt.Println(ok, v)
                } else {
                    fmt.Println("~~~~")
                }
            }
        }
    }()

    //time.Sleep(2 * time.Second)

    ch1 <- "cccc"
    ch2 <- "dddd"

    time.Sleep(1 * time.Second)
    fmt.Println("end")

}

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

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

4774 次点击  ∙  1 赞  
加入收藏 微博
17 回复  |  直到 2015-03-22 15:25:47
xuanbao
xuanbao · #1 · 10年之前
stansun
stansun · #2 · 10年之前

这个程序 有问题,你的 case 的 else 部分永远得不到执行。所以 根本不能证明什么。给你加上 close(ch1);close(ch2)就可以了

package main

import "fmt"

import "time"

func main() {

    //定义两个字符串类型的channel
    ch1, ch2 := make(chan string, 0), make(chan string, 0)

    //启用goruntine不断从两个channel中读取数据
    go func() {
        fmt.Println("start")
        time.Sleep(5 * time.Second)
        fmt.Println("wait")
        time.Sleep(2 * time.Second)
        //i := 1
        for {
            //  fmt.Println(i)
            //  i++
            select {
            case v, ok := <-ch1:
                if ok {
                    fmt.Println(ok, v)
                } else {
                    fmt.Println("!!!!")
                }
            case v, ok := <-ch2:
                if ok {
                    fmt.Println(ok, v)
                } else {
                    fmt.Println("~~~~")
                }
            }
        }
    }()

    time.Sleep(2 * time.Second)

    ch1 <- "cccc"
    ch2 <- "dddd"
    close(ch1)
    close(ch2)

    time.Sleep(1 * time.Second)
    fmt.Println("end")

}
88250
88250 · #3 · 10年之前

麻烦贴一下 Playground 分享的链接。

polaris
polaris · #4 · 10年之前

你这里明显有问题,想要实现你说的效果,channel得定义为有缓存的,如:ch1, ch2 := make(chan string, 1), make(chan string, 1)

88250
88250 · #5 · 10年之前

我想这样应该能清晰说明你想要证明的问题了:

<iframe style="border:1px solid" src="https://wide.b3log.org/playground/aa049199d5efd71cb331a75cddf66ef2.go?embed=true&#34; width="100%" height="600"></iframe>

663280439
663280439 · #6 · 10年之前
stansunstansun #2 回复

这个程序 有问题,你的 case 的 else 部分永远得不到执行。所以 根本不能证明什么。给你加上 close(ch1);close(ch2)就可以了 ```go package main import "fmt" import "time" func main() { //定义两个字符串类型的channel ch1, ch2 := make(chan string, 0), make(chan string, 0) //启用goruntine不断从两个channel中读取数据 go func() { fmt.Println("start") time.Sleep(5 * time.Second) fmt.Println("wait") time.Sleep(2 * time.Second) //i := 1 for { // fmt.Println(i) // i++ select { case v, ok := <-ch1: if ok { fmt.Println(ok, v) } else { fmt.Println("!!!!") } case v, ok := <-ch2: if ok { fmt.Println(ok, v) } else { fmt.Println("~~~~") } } } }() time.Sleep(2 * time.Second) ch1 <- "cccc" ch2 <- "dddd" close(ch1) close(ch2) time.Sleep(1 * time.Second) fmt.Println("end") } ```

其实我并没想让else执行,而且如果把ch1和ch2关闭,goroutine中的for将出现死循环,直到程序退出吧?因为close之后,select将永远拿到零值。这是其一,其二,能否给一个例子,说明select是随机从channel中取数据的?

663280439
663280439 · #7 · 10年之前
polarispolaris #4 回复

你这里明显有问题,想要实现你说的效果,channel得定义为有缓存的,如:`ch1, ch2 := make(chan string, 1), make(chan string, 1)`

为什么?goroutine本身就在读取数据,这就意味着ch1 <- "cccc",ch2 <- "dddd"执行之后,goroutine所执行的函数可以在1秒之后,读取channel的数据。这个时候,相当于两个channel都有数据,select不是应该随机取一个吗?

663280439
663280439 · #8 · 10年之前
8825088250 #3 回复

麻烦贴一下 Playground 分享的链接。

663280439
663280439 · #9 · 10年之前
8825088250 #3 回复

麻烦贴一下 Playground 分享的链接。

<iframe style="border:1px solid" src="https://wide.b3log.org/playground/ffcc15158f19aae4327982f4329499cd.go?embed=true&#34; width="100%" height="600"></iframe>

663280439
663280439 · #10 · 10年之前
8825088250 #5 回复

我想这样应该能清晰说明你想要证明的问题了:

好像还是不可以,是我哪里理解有问题吗?select的随机取值,按照你提供的代码来看,最后应该输出ddd和ccc顺序每次都不一样的。可我执行了十几次,每次结果都是ddd之后才ccc啊

stansun
stansun · #11 · 10年之前
663280439663280439 #6 回复

#2楼 @stansun 其实我并没想让else执行,而且如果把ch1和ch2关闭,goroutine中的for将出现死循环,直到程序退出吧?因为close之后,select将永远拿到零值。这是其一,其二,能否给一个例子,说明select是随机从channel中取数据的?

select永远拿0值,从两个 case 里面随机拿。说明进入两个 case 是随机的。

88250
88250 · #12 · 10年之前

多执行几次: http://dwz.cn/EmIiu

polaris
polaris · #13 · 10年之前
663280439663280439 #7 回复

#4楼 @polaris 为什么?goroutine本身就在读取数据,这就意味着ch1 <- "cccc",ch2 <- "dddd"执行之后,goroutine所执行的函数可以在1秒之后,读取channel的数据。这个时候,相当于两个channel都有数据,select不是应该随机取一个吗?

你想当然了。无缓存的channel,并不是你按你说的执行的。

zlm3110561045
zlm3110561045 · #14 · 10年之前

无缓存,到ch1必定阻塞,所以不用说,ch1的内容先输出,肯定达不到你想要测试的预期

663280439
663280439 · #15 · 10年之前
zlm3110561045zlm3110561045 #14 回复

无缓存,到ch1必定阻塞,所以不用说,ch1的内容先输出,肯定达不到你想要测试的预期

无缓存ch1必定阻塞没错,关键是读取ch1的是goroutine。这个 @88250 已经给予解答了

663280439
663280439 · #16 · 10年之前
8825088250 #12 回复

多执行几次: http://dwz.cn/EmIiu

达到我想要的预期结果了,看来还是我试的次数少。非常感谢~

xuanbao
xuanbao · #17 · 10年之前

@663280439 你不觉得 @88250 的解答,是把无缓存的 channel 改为了 有缓存的 channel 了吗?

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