golang 管道

蛐蛐儿阳 · · 1875 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

    senderOnly := make(chan<- int)   // 只能用来发送(管道的入口,只进不出)
    receiverOnly := make(<-chan int) // 只能用来接收(管道的出口,只出不进)
    unbuffer := make(chan int)       // 无缓冲可收发
    buffer := make(chan int, 2)      // 无缓冲可收发
    println(senderOnly, receiverOnly, unbuffer, buffer)

打印的是四个内存地址, 发现方法println, 和fmt.Println
区别在于一个输出到stderr,另一个是stdout,一个有返回值,另外一个没有。一个用fmt包支持,另外一个可能将来就没有了。

使用场景1 等待goroutine完成

理解了 go func 假如执行很长时间, main中只要其他代码逻辑执行完, goroutine不管有没有执行完都会推出。管道可以等他执行完了以后再退出。
源代码

package main

import "fmt"

func main()  {
    // 声明
    chan1 := make(chan bool)
    fmt.Println("func main start")
    go func() {
        fmt.Println("do something")
        // 发送true值
        chan1 <- true
    }()
    // 接收
    <- chan1
    close(chan1)
    fmt.Println("end func main")
}

以上是源代码,为了好玩,我们改下

package main

import (
    "fmt"
    "time"
)

func main()  {
    // 声明
    chan1 := make(chan bool)
    fmt.Println("func main start")
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("do something")
            time.Sleep(time.Second)
        }
        // 发送true值
        chan1 <- true
    }()
    // 接收
    <- chan1
    close(chan1)
    fmt.Println("end func main")
}

为了说明问题,我们把管道去掉。

package main

import (
    "fmt"
    "time"
)

func main()  {
    // 声明
    //chan1 := make(chan bool)
    fmt.Println("func main start")
    go func() {
        for i := 0; i < 10; i++ {
            fmt.Println("do something")
            time.Sleep(time.Second)
        }
        // 发送true值
        //chan1 <- true
    }()
    // 接收
    //<- chan1
    //close(chan1)
    fmt.Println("end func main")
}

可以看到代码输出

func main start
end func main

go 里面根本没运行。

2, 多个go协同

和程序里面提到的管道一样, 一个go的输出是另一个的输入。 利用管道在多个go中传值。
ok, 不是这个例子,例子中三块 go逻辑在操纵一个变量x, x被放入管道后, 三个go func 会对其进行累计处理。让我来写一个类似代码。

package main

func main()  {
    chanString := make(chan string)

    // 这是个结巴, 大概说了三次可
    s := "我"
    go func() {
        println("come into goroutine1")
        for i := 0; i<3; i++ {
            s += "可"
        }
        chanString <- s
    }()

    // 说了两个能
    go func() {
        println("come into goroutine2")
        for j := 0; j < 2; j++ {
            s += "能"
        }
        chanString <- s
    }()

    //说了四个要
    go func() {
        println("come into goroutine3")
        for j := 0; j < 2; j++ {
            s += "要"
        }
        chanString <- s
    }()

    // 注意这段代码
    var result string
    for i := 0; i < 3; i++ {
        result += <-chanString
    }
    close(chanString)
    println(result)
}

比较难理解的是 最后一个for循环。
尝试理解下, 第一,二个程序其实都利用了管道的 (放出原则), 就是接收时,

<- chan

他没接收到就会挂起,等待接收,所以程序不会立刻退出,都是接收完了才退出。
那个for循环,为啥必须写3, 写4,或2都会报错呢?写2是几率型报错。
是因为你有三个 go func。 其实里面的for是无意义的,
这样看简单点

package main

func main()  {
    chanString := make(chan string)

    // 这是个结巴, 大概说了三次可
    s := "我"
    go func() {
        println("come into goroutine1")
        s += "可"
        chanString <- s
    }()

    // 说了两个能
    go func() {
        println("come into goroutine2")
        s += "能"
        chanString <- s
    }()

    //说了四个要
    go func() {
        println("come into goroutine3")
        s += "要"
        chanString <- s
    }()

    // 注意这段代码
    var result string
    for i := 0; i < 1; i++ {
        result += <-chanString
    }
    close(chanString)
    println(result)
}

就是如果for i《1 时,报错,

panic: send on closed channel

我往管道发送值,但已经没人接了, 他只接收了一条。
当for 4次的时候,

fatal error: all goroutines are asleep - deadlock!

所有 goroutines 已完毕, 你还在挂起等待接收,所以报错。

3 select

两个goroutine无直接关联,但其中一个先达到某一设定条件便退出或超时退出

package main

import "time"

func main() {
    println("start main")
    cond1 := make(chan int)
    cond2 := make(chan uint64)

    go func() {
        for i := 0; ; i++ {
            cond1 <- i
        }
    }()

    go func() {
        var i uint64
        for ; ; i++ {
            cond2 <- i
        }
    }()

    endCond := false
    for endCond != true {
        select {
        case a := <-cond1:
            if a > 99 {
                println("end with cond1")
                endCond = true
            }
        case b := <-cond2:
            if b == 100 {
                println("end with cond2")
                endCond = true
            }
        case <-time.After(time.Microsecond):
            println("end with timeout")
            endCond = true
        }
    }

    println("end main")
}

很有意思的代码,利用

for endCond != true
一旦
endCond = true 
输出
println("end main"), 整个程序退出。


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

本文来自:简书

感谢作者:蛐蛐儿阳

查看原文:golang 管道

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

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