- 前言
- 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.4.5 指定通道的执行顺序
尽管您不应该对 goroutines 的执行顺序做任何假设,但当您需要控制这个顺序时是可以的。这小节介绍一个使用 信号通道 的技巧。
您可能会问为什么简单函数能够更容易的控制执行顺序,还要选择按顺序执行 goroutines。这个答案很简单:goroutines 能够操作并发并等待其他 goroutines 结束,而在普通的函数却不能!
说明这个主题的 Go 程序命名为 defineOrder.go
,并分了五部分来介绍。
defineOrder.go
的第一部分如下:
package main
import (
"fmt"
"time"
)
func A(a, b chan struct{}) {
<-a
fmt.Println("A()!")
time.Sleep(time.Second)
close(b)
}
A()
函数能被存储在 a
参数的通道中并阻塞。一旦在 main
函数中通道被解除阻塞,函数 A()
将开始工作。最后,它关闭 b
通道,解除在函数 B()
中的另个函数的阻塞。
defineOrder.go
的第二段代码如下:
func B(a, b chan struct{}) {
<-a
fmt.Println("B()!")
close(b)
}
B()
中的逻辑和函数 A()
相同。这个函数被阻塞直到 a
通道关闭。然后它做自己的工作并关闭 b
通道。注意 a
和 b
通道值这个函数的参数名。
defineOrder.go
的第三段代码如下:
func C(a chan struct{}) {
<-a
fmt.Println("C()!")
}
C()
函数被阻塞并等待 a
通道关闭再开始工作。
defineOrder.go
的第四部分如下:
func main() {
x := make(chan struct{})
y := make(chan struct{})
z := make(chan struct{})
这三个通道作为三个函数的参数。
defineorder.go
的最后一段如下:
go C(z)
go A(x, y)
go C(z)
go B(y, z)
go C(z)
close(x)
time.Sleep(3 * time.Second)
}
执行 defineOrder.go
将产生期望的输出,即使 C()
函数被调用了多次:
$go run defineOrder.go
A()!
B()!
C()!
C()!
C()!
C()!
以 goroutines 调用 C()
函数多次会工作的很好,因为 C()
没有关闭任何通道。然而,如果您调用 A()
或 B()
多次的话,您可能会得到如下错误信息:
如您所见,A()
函数被调用了俩次。然而,由于 A()
函数关闭了一个通道,它的 goroutines 中的一个将发现通道已经关闭并产生一个崩溃。如果您调用 B
多次会得到同样的崩溃信息。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论