- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
10.5.3 通过 goroutine 共享内存
本主题的最后一小节说明了如何使用专用的goroutine共享数据。尽管,共享内存是线程间彼此通信的传统方法,但 Go 具有内置的同步功能,允许单个 goroutine 拥有共享数据。这意味着其他 goroutines 必须发送消息给这个拥有共享数据的单独 goroutine,这可以防止数据损坏。这样的 goroutine 叫 监视器 goroutine。这 Go 的术语中,这是通过通信来共享而不是通过共享来通信。
该技术通过使用 monitor.go
源文件来说明,分五部分来介绍。monitor.go
程序使用监视器 goroutine 随机产生数字。
monitor.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
的第二段代码如下:
func set(newValue int) {
writeValue <-newValue
}
func read() int {
return <-readValue
}
set()
函数的目的是共享变量的值,而 read()
函数的目的是读取已保存变量的值。
monitor.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
的第四部分代码如下:
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
的最后一段代码如下:
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 的哲学思想
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论