分享

通道,通道缓冲,通道同步,通道方向,通道选择器

 心平9bwburua3p 2017-04-14

通道

代码实例

// _通道_ 是连接多个 Go 协程的管道。你可以从一个 Go 协程
// 将值发送到通道,然后在别的 Go 协程中接收。

package main

import "fmt"

func main() {

    // 使用 `make(chan val-type)` 创建一个新的通道。
    // 通道类型就是他们需要传递值的类型。
    messages := make(chan string)

    // 使用 `channel <-` 语法 _发送_ 一个新的值到通道中。这里
    // 我们在一个新的 Go 协程中发送 `"ping"` 到上面创建的
    // `messages` 通道中。
    go func() { messages <- "ping" }()

    // 使用 `<-channel` 语法从通道中 _接收_ 一个值。这里
    // 将接收我们在上面发送的 `"ping"` 消息并打印出来。
    msg := <-messages
    fmt.Println(msg)
}

运行程序

# 我们运行程序时,通过通道,消息 `"ping"` 成功的从一个 Go 协程传到
# 另一个中。
$ go run channels.go
ping

# 默认发送和接收操作是阻塞的,直到发送方和接收方都准备完毕。
# 这个特性允许我们,不使用任何其它的同步操作,来在程序结尾等待
# 消息 `"ping"`。

通道缓冲

代码实例

// 默认通道是 _无缓冲_ 的,这意味着只有在对应的接收(`<- chan`)
// 通道准备好接收时,才允许进行发送(`chan <-`)。_可缓存通道_
// 允许在没有对应接收方的情况下,缓存限定数量的值。

package main

import "fmt"

func main() {

    // 这里我们 `make` 了一个通道,最多允许缓存 2 个值。
    messages := make(chan string, 2)

    // 因为这个通道是有缓冲区的,即使没有一个对应的并发接收
    // 方,我们仍然可以发送这些值。
    messages <- "buffered"
    messages <- "channel"

    // 然后我们可以像前面一样接收这两个值。
    fmt.Println(<-messages)
    fmt.Println(<-messages)
}

运行程序

$ go run channel-buffering.go 
buffered
channel

通道同步

代码实例

// 我们可以使用通道来同步 Go 协程间的执行状态。这里是一个
// 使用阻塞的接受方式来等待一个 Go 协程的运行结束。

package main

import "fmt"
import "time"

// 这是一个我们将要在 Go 协程中运行的函数。`done` 通道
// 将被用于通知其他 Go 协程这个函数已经工作完毕。
func worker(done chan bool) {
    fmt.Print("working...")
    time.Sleep(time.Second)
    fmt.Println("done")

    // 发送一个值来通知我们已经完工啦。
    done <- true
}

func main() {

    // 运行一个 worker Go协程,并给予用于通知的通道。
    done := make(chan bool, 1)
    go worker(done)

    // 程序将在接收到通道中 worker 发出的通知前一直阻塞。
    <-done
}

运行程序

$ go run channel-synchronization.go
working...done

# 如果你把 `<- done` 这行代码从程序中移除,程序甚至会在 `worker`
# 还没开始运行时就结束了。

通道方向

代码实例

// 当使用通道作为函数的参数时,你可以指定这个通道是不是
// 只用来发送或者接收值。这个特性提升了程序的类型安全性。

package main

import "fmt"

// `ping` 函数定义了一个只允许发送数据的通道。尝试使用这个通
// 道来接收数据将会得到一个编译时错误。
func ping(pings chan<- string, msg string) {
    pings <- msg
}

// `pong` 函数允许通道(`pings`)来接收数据,另一通道
// (`pongs`)来发送数据。
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}

func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}

运行程序

$ go run channel-directions.go
passed message

通道选择器

代码实例

// Go 的_通道选择器_ 让你可以同时等待多个通道操作。
// Go 协程和通道以及选择器的结合是 Go 的一个强大特性。

package main

import "time"
import "fmt"

func main() {

    // 在我们的例子中,我们将从两个通道中选择。
    c1 := make(chan string)
    c2 := make(chan string)

    // 各个通道将在若干时间后接收一个值,这个用来模拟例如
    // 并行的 Go 协程中阻塞的 RPC 操作
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()

    // 我们使用 `select` 关键字来同时等待这两个值,并打
    // 印各自接收到的值。
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

运行程序

# 我们首先接收到值 `"one"`,然后就是预料中的 `"two"`
# 了。
$ time go run select.go 
received one
received two

# 注意从第一次和第二次 `Sleeps` 并发执行,总共仅运行了
# 两秒左右。
real    0m2.245s
动手实践是学习 IT 技术最有效的方式! 开始实验

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多