Go语言之通道(一)

1. 开篇

我决定学习Go语言的时候,就做好了多线程编程的准备,而多线程编程,很重要的一点就是线程间通信。比如Java就用了notify进行通信,线程间通信是一个很重要的特性,没有通信那么多线程编程意义也不太大。

昨天我的学习笔记里提到了goroutine,我发现goroutine是如此简单的实现了所谓的并发,那么Go就一定有简单的方式实现goroutine间的通信,这个机制就是所谓的通道。

学过数据结构的人应该对FIFO这个特性特别熟悉,一般队列会有这个特点,而通道也可以理解成一个FIFO的队列,先被发送到通道中的值先被接收。

下面的图来自《Go IN ACTION》,很形象的说明了通道:

无缓冲通道

通道有一个很重要的运算符:<-,当运算符写在通道变量的右边时,表示将一个值传入通道,当运算符在通道变量左边时,表示从通道中取出值。

2. 例子

上面图中对无缓冲通道的描述倒是很像一个打乒乓球的过程,你来我往的。

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

var wg sync.WaitGroup

func init() {
    rand.Seed(time.Now().UnixNano())
}

func main() {
    ch := make(chan int)
    wg.Add(2)
    go playPingPong("Wong", ch)
    go playPingPong("Lee", ch)
    ch <- 1
    wg.Wait()

}

func playPingPong(player string, ch chan int) {
    defer wg.Done()

    for {
        ball, ok := <-ch
        if !ok {
            fmt.Printf("%s has won!\n", player)
            return
        }
        n := rand.Intn(100)
        if n % 13 == 0 {
            fmt.Printf("player %s missed!\n", player)
            close(ch)
            return
        }
        ball++
        fmt.Printf("play %s has hit the ball, his score is %d\n", player, ball)

        ch <- ball
    }
}

首先还是实现了两个goroutine,不同的是,方法里有一个参数是通道了。

ch<-1相当于发球的操作,此时ball这个变量就开始在通道里来回游走进行通信了,如果发现有人丢球了,就调度close方法将通道关闭,另外一个player发现通道关闭了,证明对方丢球了,因此也就能宣布自己胜利了。

今天学习的是无缓冲通道,发送和接收必须是同时的,这有点类似于程序设计中的同步操作。

下面写一个有缓冲的通道的例子,作为明天笔记的引子吧:

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

虽然在完成了向通道发送数值的操作之后,就关闭了通道,但是后面的三个打印语句仍然可以按照1,2,3的顺序打印出正确的数值。

有点像消息队列了,生产者只管生产,至于消费者如何是不关心的,即便是通道关闭了,不能再发送数值了,也不影响消费者消费。

这还有点像MySQL的传统异步复制,Master上推binlog的线程推完,服务器宕机了没有关系,反正已经写到Slave的中继日志上了,数据最终会达成一致。

3. 小结

每天写小结真累,但是Go语言真的是有意思,让我在尝试了多种语言之后终于决定将其作为未来主要的语言,用于辅助我的DBA工作。

你可能感兴趣的:(Go语言之通道(一))