Go 学习笔记(二十六)指定 goroutine 的执行顺序

本文原创地址:博客园骏马金龙Go 基础系列:指定 goroutine 的执行顺序

当关闭一个 channel 时,会使得这个 channel 变得可读。通过这个特性,可以实现一个 goroutine 执行顺序的技巧。

如果一个 goroutine A 依赖于另一个 goroutine B,在 goroutine A 中首先通过读 goroutine B 来阻塞自己,直到 goroutine B 关闭自身之后,goroutine A 才会继续运行。这样,goroutine B 就先于 goroutine A 运行。

下面是一个指定 goroutine 执行顺序的示例,它保证的顺序是A()-->B()-->C()

package main

import (
	"fmt"
	"time"
)

// A首先被a阻塞,A()结束后关闭b,使b可读
func A(a, b chan struct{}) {
	<-a
	fmt.Println("A()!")
	time.Sleep(time.Second)
	close(b)
}

// B首先被a阻塞,B()结束后关闭b,使b可读
func B(a, b chan struct{}) {
	<-a
	fmt.Println("B()!")
	close(b)
}

// C首先被a阻塞
func C(a chan struct{}) {
	<-a
	fmt.Println("C()!")
}

func main() {
	x := make(chan struct{})
	y := make(chan struct{})
	z := make(chan struct{})

	go C(z)
	go A(x, y)
	go C(z)
	go B(y, z)
	go C(z)
	
	// 关闭x,让x可读
	close(x)
	time.Sleep(3 * time.Second)
}

上面的示例中:A goroutine 被 x 阻塞,B goroutine 被 y 阻塞,C goroutine 被 z 阻塞。C 依赖的 z 由 B 关闭,B 依赖的 y 由 A 关闭。

如此一来,当 main goroutine 中的 x 被关闭后,A()从阻塞中释放,继续执行,关闭 y,然后 B 从阻塞中释放,继续执行,关闭 z,C 得以释放。由于 z 被关闭后,z 仍然可读,所以多次执行 C(z) 不会出问题。

A()和 B() 不能多次执行,因为 close() 不能操作已被关闭的 channel。

注意,上面的 channel 都是struct{}类型的,整个过程中,x、y、z 这 3 个通道都没有传递数据,而是直接关闭来释放通道,让某些阻塞的 goroutine 继续执行下去。显然,这里的 x、y、z 的作用都是 "信号通道",用来传递消息。

上一篇 Go 学习笔记(二十五)双层 channel 用法示例
Go 学习笔记(目录)
下一篇 Go 学习笔记(二十七)互斥锁 Mutex 和读写锁 RWMutex 用法详述