文章目录


Java中的门闩最基础的实现方式就是CountDownLatch,可以参考之前的文章《使用三个线程,按顺序打印X,Y,Z,连续打印10次》; 而Go中可以通过sync.WaitGroup来实现门闩。

先来看看Go语言标准库文档中对WaitGroup的描述:
【WaitGroup】用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。

【Add】方法向内部计数加上delta,delta可以是负数;如果内部计数器变为0,Wait方法阻塞等待的所有线程都会释放,如果计数器小于0,方法panic。注意Add加上正数的调用应在Wait之前,否则Wait可能只会等待很少的线程。一般来说本方法应在创建新的线程或者其他应等待的事件之前调用。

【Done】方法减少WaitGroup计数器的值,应在线程的最后执行。

【Wait】方法阻塞直到WaitGroup计数器减为0。

通过上面的描述可以发现他和Java的CountDownLatch作用很像。

Add(X)方法对应new CountDownLatch(X),代表设置计数器

Done()方法对应CountDownLatch.countDown(),代表计数器减1

Wait()方法对应CountDownLatch.await(),代表在这里阻塞住

为了和Java的门闩形成对比,这里用Go的WaitGroup实现“使用三个线程,按顺序打印X,Y,Z,连续打印10次”的功能。

package main

import (
	"fmt"
	"sync"
)

func main() {
	// 打印X的协程门闩
	countLatchX := sync.WaitGroup{}
	countLatchX.Add(1)

	// 打印Y的协程门闩
	countLatchY := sync.WaitGroup{}
	countLatchY.Add(1)

	// 打印Z的协程门闩
	countLatchZ := sync.WaitGroup{}
	countLatchZ.Add(1)

	// 总门闩
	countLatchMain := sync.WaitGroup{}
	countLatchMain.Add(3)

	// 打印X的协程
	go func() {
		for i:=0; i<10; i++{
			// X门闩如果计数>0则阻塞
			countLatchX.Wait()
			fmt.Println("print-x:X")
			// 让Y门闩计数减1,从而让Y协程打印Y
			countLatchY.Done()
			// 让X协程下一次打印X前阻塞
			countLatchX.Add(1)
		}
		countLatchMain.Done()
	}()

	// 打印Y的协程
	go func() {
		for i:=0; i<10; i++{
			// Y门闩如果计数>0则阻塞
			countLatchY.Wait()
			fmt.Println("print-y:Y")
			// 让Z门闩计数减1,从而让Z协程打印Z
			countLatchZ.Done()
			// 让Y协程下一次打印Y前阻塞
			countLatchY.Add(1)
		}
		countLatchMain.Done()
	}()

	// 打印Z的协程
	go func() {
		for i:=0; i<10; i++{
			// Z门闩如果计数>0则阻塞
			countLatchZ.Wait()
			fmt.Println("print-z:Z")
			fmt.Println("-----------")
			// 让X门闩计数减1,从而让X协程打印X
			countLatchX.Done()
			// 让Z协程下一次打印Z前阻塞
			countLatchZ.Add(1)
		}
		countLatchMain.Done()
	}()

	// 让门闩X计数减1,从而让X协程先打印X
	countLatchX.Done()

	// 阻塞等待所有协程打印完毕,这里区别于Java,Java中主线程结束,非后台子线程可以继续执行;Go主线程结束,创建的协程也就结束了
	countLatchMain.Wait()
}

执行结果如下:

print-x:X
print-y:Y
print-z:Z
-----------
print-x:X
print-y:Y
print-z:Z
-----------
print-x:X
print-y:Y
print-z:Z
-----------
print-x:X
print-y:Y
print-z:Z
-----------
print-x:X
print-y:Y
print-z:Z
-----------
print-x:X
print-y:Y
print-z:Z
-----------