golang控制并发(和)

时间:2024-10-01 14:06:38

我们都知道,在golang中很容易实现并发,只需要一个关键字go就可以。但是在开启了多个goruntine之后,我们要如何去管理它们呢(包括停止退出goroutine,等待goruntine执行完成,继续让goruntine执行等)。这些我们在日常的业务中都可能碰到,下面就讲讲在不同的场景如何正确优雅地管理goruntine,即控制并发。

一,sync.WaitGroup

作用: 任务编排,等待多个 goroutine 全部完成

适用场景: 好多个 goroutine 协同做一件事情的时候,因为每个 goroutine 做的都是这件事情的一部分,只有全部的 goroutine 都完成,这件事情才算是完成,这是等待的方式。

func main() {
	var wg 

	(2)
	go func() {
		(2 * )
		("1号完成")
		()
	}()
	go func() {
		(2 * )
		("2号完成")
		()
	}()
	()
	("好了,大家都干完了,放工")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这部分比较简单,不过多描述。更多参见在异步读写中使用

二,

上面的场景是等待多个小任务都执行完毕之后退出。下面考虑这种场景:有一个持续性的任务,一般是常驻的不退出,比如说监控任务。但有时可能因为特殊需求,需要根据条件判断后让其退出。因为该监控任务和其他任务没有数据连接(channel之类的),但靠是不能使其退出的。 很自然的,我们能想到应该通过一个媒介,来通知监控任务退出。全局变量可以充当这个媒介,但鉴于安全性很差,这里不予考虑(代码中一般来说不允许出现全局变量)。

chan通知

func main() {
	stop := make(chan bool)

	go func() {
		for {
			select {
			case <-stop:
				("监控退出,停止了...")
				return
			default:
				("goroutine监控中...")
				(2 * )
			}
		}
	}()

	(10 * )
	("可以了,通知监控停止")
	stop <- true
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	(5 * )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

输出结果:

goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
可以了,通知监控停止
监控退出,停止了...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这种方式还是比较优雅的,但还是有很大的局限性。如果我们需要控制很多的goruntine结束怎么办,难道我们要搞出很多个chan?又或者子goruntine中又衍生出更多的子goruntine怎么办?如果一层层的无穷尽的 goroutine 呢?这就非常复杂了,即使我们定义很多 chan 也很难解决这个问题,因为 goroutine 的关系链就导致了这种场景非常复杂。

初识Context
上面说的这种场景是存在的,比如一个网络请求 Request,每个 Request 都需要开启一个 goroutine 做一些事情,这些 goroutine 又可能会开启其它的 goroutine。所以我们需要一种可以跟踪 goroutine 的方案,才可以达到控制它们的目的,这就是Go语言为我们提供的 Context,称之为上下文非常贴切,它就是 goroutine 的上下文。

利用重写上面示例:

func main() {
	ctx, cancel := (())
	go func(ctx ) {
		for {
			select {
			case <-():
				("监控退出,停止了...")
				return
			default:
				("goroutine监控中...")
				(2 * )
			}
		}
	}(ctx)

	(10 * )
	("可以了,通知监控停止")

	cancel()
	
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	(5 * )
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Context 控制多个 goroutine

func main() {
	ctx, cancel := (())
	go watch(ctx, "【监控1】")
	go watch(ctx, "【监控2】")
	go watch(ctx, "【监控3】")

	(10 * )
	("可以了,通知监控停止")
	cancel()
	//为了检测监控过是否停止,如果没有监控输出,就表示停止了
	(5 * )
}

func watch(ctx , name string) {
	for {
		select {
		case <-():
			(name, "监控退出,停止了...")
			return
		default:
			(name, "goroutine监控中...")
			(2 * )
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25