> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/10.0/10.5/10.5.3.md).

# 10.5.3 通过goroutine共享内存

本主题的最后一小节说明了如何使用专用的goroutine共享数据。尽管，共享内存是线程间彼此通信的传统方法，但 Go 具有内置的同步功能，允许单个 goroutine 拥有共享数据。这意味着其他 goroutines 必须发送消息给这个拥有共享数据的单独 goroutine，这可以防止数据损坏。这样的 goroutine 叫 **监视器 goroutine**。这 Go 的术语中，这是通过通信来共享而不是通过共享来通信。

该技术通过使用 `monitor.go` 源文件来说明，分五部分来介绍。`monitor.go` 程序使用监视器 goroutine 随机产生数字。

`monitor.go` 的第一部分如下：

```go
package main

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

var readValue = make(chan int)
var writeValue = make(chan int)
```

`readValue` 管道用于读取随机数，而 `writeValue` 管道用于得到新随机数。

`monitor.go` 的第二段代码如下：

```go
func set(newValue int) {
    writeValue <-newValue
}

func read() int {
    return <-readValue
}
```

`set()` 函数的目的是共享变量的值，而 `read()` 函数的目的是读取已保存变量的值。

`monitor.go` 程序的第三段代码如下：

```go
func monitor() {
    var value int

    for {
        select {
            case newValue := <-writeValue:
                value = newValue
                fmt.Printf("%d", value)
            case readValue <- value:
        }
    }
}
```

这个程序的所有逻辑都在这个 `monitor()` 函数里了。最具体地说，`select` 语句编排了整个程序的操作。当您有读请求时，`read()` 函数试图从由 `monitor()` 函数控制的 `readValue` 管道读取数据。返回保存在 `value` 变量中的当前值。另一方面，当您想要修改存储值时，可以调用 `set()`。往 `writeValue` 管道写数据也由 `select` 语句处理。结果就是，不使用 `monitor()` 函数没人可以处理 `value` 共享变量。

`monitor.go` 的第四部分代码如下：

```go
func main() {
    if len(os.Args) != 2 {
        fmt.Println("Please give an integer!")
        return
    }
    n, err := strconv.Atoi(os.Args[1])
    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Printf("Going to create %d random numbers.\n", n)
    rand.Seed(time.Now().Unix())
    go monitor()
```

`monitor.go` 的最后一段代码如下：

```go
    var w sync.WaitGroup
    for r := 0; r < n; r++ {
        w.Add(1)
        go func() {
            defer w.Done()
            set(rand.Intn(10 * n))
        }()
    }
    w.Wait()
    fmt.Printf("\nLast value: %d\n", read())
}
```

执行 `monitor.go` 产生如下输出：

```
$go run monitor.go 20
Going to create 20 random numbers.
89 88 166 42 149 89 20 84 44 178 184 28 52 121 62 91 31c117 140 106
Last value: 106
$go run monitor.go 10
Going to create 10 random numbers.
30 16 66 70 65 45 31 57 62 26
Last value: 26
```

> *个人而言，我更喜欢使用监视器 goroutine 而不是传统的共享内存技术，因为使用监视器 goroutine 的实现更安全，更贴合 Go 的哲学思想*
