英文:
Golang pattern to kill multiple goroutines at once
问题
我有两个goroutine,如下所示。我想要同步它们,以便当一个goroutine返回时,另一个goroutine也应该退出。在Go语言中,最好的方法是什么?
func main() {
go func() {
...
if err != nil {
return
}
}()
go func() {
...
if err != nil {
return
}
}()
}
我在这里模拟了这种情况https://play.golang.org/p/IqawStXt7rt,并尝试使用通道来信号化一个goroutine已完成。这看起来可能会导致对关闭的通道进行写入,从而导致恐慌。解决这个问题的最佳方法是什么?
英文:
I have two goroutines as shown in the snippet below. I want to synchronize them such that when one returns, the other one should also exit. What is best way in go to achieve this?
func main() {
go func() {
...
if err != nil {
return
}
}()
go func() {
...
if err != nil {
return
}
}()
}
I have simulated this scenario here https://play.golang.org/p/IqawStXt7rt and tried to solve it with a channel to signal a routine is done. This looks like there can be a write to closed channel resulting in a panic. What is the best way to solve this problem?
答案1
得分: 22
你可以使用上下文(context)在两个Go协程之间进行通信。
例如,
package main
import (
"context"
"sync"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(3)
go func() {
defer wg.Done()
for {
select {
// 来自其他Go协程的消息完成
case <-ctx.Done():
// 结束
}
}
}()
go func() {
defer wg.Done()
for {
select {
// 来自其他Go协程的消息完成
case <-ctx.Done():
// 结束
}
}
}()
go func() {
defer wg.Done()
// 进行你的操作
// 当这个Go协程结束时调用cancel
cancel()
}()
wg.Wait()
}
英文:
You can use context for communication between two go routines.
For example,
package main
import (
"context"
"sync"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(3)
go func() {
defer wg.Done()
for {
select {
// msg from other goroutine finish
case <-ctx.Done():
// end
}
}
}()
go func() {
defer wg.Done()
for {
select {
// msg from other goroutine finish
case <-ctx.Done():
// end
}
}
}()
go func() {
defer wg.Done()
// your operation
// call cancel when this goroutine ends
cancel()
}()
wg.Wait()
}
答案2
得分: 2
使用close在通道上发出完成信号。这允许多个goroutine通过在通道上接收来检查完成状态。
使用每个goroutine一个通道来表示goroutine的完成。
done1 := make(chan struct{}) // 当goroutine 1返回时关闭
done2 := make(chan struct{}) // 当goroutine 2返回时关闭
go func() {
defer close(done1)
timer1 := time.NewTicker(1 * time.Second)
defer timer1.Stop()
timer2 := time.NewTicker(2 * time.Second)
defer timer2.Stop()
for {
select {
case <-done2:
// 另一个goroutine已返回。
fmt.Println("done func 1")
return
case <-timer1.C:
fmt.Println("timer1 func 1")
case <-timer2.C:
fmt.Println("timer2 func 1")
return
}
}
}()
go func() {
defer close(done2)
for {
select {
case <-done1:
// 另一个goroutine已返回。
fmt.Println("done func 2")
return
default:
time.Sleep(3 * time.Second)
fmt.Println("sleep done from func 2")
return
}
}
}()
fmt.Println("等待goroutine完成")
// 等待两个goroutine返回。这里等待的顺序无关紧要。
<-done1
<-done2
fmt.Println("全部完成")
英文:
Use close on a channel to signal completion. This allows multiple goroutines to check for completion by receiving on the channel.
Use one channel per goroutine to signal completion of the goroutine.
done1 := make(chan struct{}) // closed when goroutine 1 returns
done2 := make(chan struct{}) // closed when goroutine 2 returns
go func() {
defer close(done1)
timer1 := time.NewTicker(1 * time.Second)
defer timer1.Stop()
timer2 := time.NewTicker(2 * time.Second)
defer timer2.Stop()
for {
select {
case <-done2:
// The other goroutine returned.
fmt.Println("done func 1")
return
case <-timer1.C:
fmt.Println("timer1 func 1")
case <-timer2.C:
fmt.Println("timer2 func 1")
return
}
}
}()
go func() {
defer close(done2)
for {
select {
case <-done1:
// The other goroutine returned.
fmt.Println("done func 2")
return
default:
time.Sleep(3 * time.Second)
fmt.Println("sleep done from func 2")
return
}
}
}()
fmt.Println("waiting for goroutines to complete")
// Wait for both goroutines to return. The order that
// we wait here does not matter.
<-done1
<-done2
fmt.Println("all done")
答案3
得分: 2
首先,将等待goroutine和done
通道分开。
使用sync.WaitGroup
来协调goroutine。
func main() {
wait := &sync.WaitGroup{}
N := 3
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true)
}
wait.Wait()
fmt.Println(`Exiting main`)
}
每个goroutine的代码如下:
// 实际goroutine的代码
func goFunc(wait *sync.WaitGroup, i int, closer bool) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-T:
fmt.Println(`Tick `, i)
if closer {
return
}
}
}
}
(https://play.golang.org/p/mDO4P56lzBU)
我们的main
函数成功地在退出之前等待goroutine退出。每个goroutine都在关闭自身,我们希望有一种方法可以同时取消所有的goroutine。
我们将使用一个chan
来实现这个,并利用通道接收的特性:
引用:关闭的通道上的接收操作总是可以立即进行,之前发送的任何值都已经被接收到后,会产生元素类型的零值。
(https://golang.org/ref/spec#Receive_operator)
我们修改我们的goroutine来检查CLOSE:
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
close(CLOSE)
}
}
}
}
然后我们改变我们的func main
,使其传递CLOSE通道,并设置closer
变量,以便只有最后一个goroutine会触发关闭:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// 启动goroutine
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, i == N, CLOSE)
}
// 等待goroutine完成
wait.Wait()
fmt.Println(`Exiting main`)
}
(https://play.golang.org/p/E91CtRAHDp2)
现在看起来一切都正常工作。
但实际上并不是这样的。并发是困难的。这段代码中潜伏着一个错误,只等着在生产环境中咬你一口。让我们找出它。
将我们的示例更改为每个goroutine都会关闭:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// 启动goroutine
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true /*** 每个goroutine都会关闭 ***/, CLOSE)
}
// 等待goroutine完成
wait.Wait()
fmt.Println(`Exiting main`)
}
将goroutine更改为在关闭之前等待一段时间。我们希望两个goroutine在同一时间关闭:
// 实际goroutine的代码
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
/*** 在关闭之前等待一段时间 ***/
time.Sleep(time.Second)
close(CLOSE)
}
}
}
}
(https://play.golang.org/p/YHnbDpnJCks)
我们遇到了崩溃:
Tick 1
Tick 2
Tick 3
Exiting 1
Exiting 2
panic: close of closed channel
goroutine 7 [running]:
main.goFunc(0x40e020, 0x2, 0x68601, 0x430080)
/tmp/sandbox558886627/prog.go:24 +0x2e0
created by main.main
/tmp/sandbox558886627/prog.go:38 +0xc0
Program exited: status 2.
虽然在关闭的通道上接收会立即返回,但是你不能关闭一个已经关闭的通道。
我们需要一些协调。我们可以使用sync.Mutex
和一个bool
来指示我们是否已经关闭了通道。让我们创建一个结构体来实现这个:
type Close struct {
C chan struct{}
l sync.Mutex
closed bool
}
func NewClose() *Close {
return &Close {
C: make(chan struct{}),
}
}
func (c *Close) Close() {
c.l.Lock()
if (!c.closed) {
c.closed=true
close(c.C)
}
c.l.Unlock()
}
重写我们的gofunc和main函数,使用我们的新Close结构体,然后我们就可以运行了:
https://play.golang.org/p/eH3djHu8EXW
并发的问题在于你总是需要考虑如果另一个“线程”在代码的任何其他地方会发生什么。
英文:
First separate the waiting on go-routines and the done
channel.
Use a sync.WaitGroup
to coordinate the goroutines.
func main() {
wait := &sync.WaitGroup{}
N := 3
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true)
}
wait.Wait()
fmt.Println(`Exiting main`)
}
Each goroutine will look like this:
// code for the actual goroutine
func goFunc(wait *sync.WaitGroup, i int, closer bool) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-T:
fmt.Println(`Tick `, i)
if closer {
return
}
}
}
}
(https://play.golang.org/p/mDO4P56lzBU)
Our main func is successfully waiting for the goroutines to exit before it exits. Each goroutine is closing itself, and we want a way to cancel all our goroutines at the same time.
We'll do this with a chan
, and make use of this feature of receiving from channels:
QUOTE: A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
(https://golang.org/ref/spec#Receive_operator)
We modify our goroutines to check for a CLOSE:
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
close(CLOSE)
}
}
}
}
and then we change our func main
so that it passes the CLOSE channel through, and we'll set the closer
variable so that only the last of our goroutines will trigger the close:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// Launch the goroutines
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, i == N, CLOSE)
}
// Wait for the goroutines to finish
wait.Wait()
fmt.Println(`Exiting main`)
}
(https://play.golang.org/p/E91CtRAHDp2)
Now it looks like everything is working.
But it isn't. Concurrency is hard. There's a bug lurking in this code, just waiting to bite you in production. Let's surface it.
Change our example so that every goroutine will close:
func main() {
wait := &sync.WaitGroup{}
N := 3
CLOSE := make(chan struct{})
// Launch the goroutines
wait.Add(N)
for i := 1; i <= N; i++ {
go goFunc(wait, i, true /*** EVERY GOROUTINE WILL CLOSE ***/, CLOSE)
}
// Wait for the goroutines to finish
wait.Wait()
fmt.Println(`Exiting main`)
}
Change goroutine so that it takes a while before closing. We want two goroutines to be about to close at the same time:
// code for the actual goroutine
func goFunc(wait *sync.WaitGroup, i int, closer bool, CLOSE chan struct{}) {
defer wait.Done()
defer fmt.Println(`Exiting `, i)
T := time.Tick(time.Duration(100*i) * time.Millisecond)
for {
select {
case <-CLOSE:
return
case <-T:
fmt.Println(`Tick `, i)
if closer {
/*** TAKE A WHILE BEFORE CLOSING ***/
time.Sleep(time.Second)
close(CLOSE)
}
}
}
}
(https://play.golang.org/p/YHnbDpnJCks)
We crash with:
Tick 1
Tick 2
Tick 3
Exiting 1
Exiting 2
panic: close of closed channel
goroutine 7 [running]:
main.goFunc(0x40e020, 0x2, 0x68601, 0x430080)
/tmp/sandbox558886627/prog.go:24 +0x2e0
created by main.main
/tmp/sandbox558886627/prog.go:38 +0xc0
Program exited: status 2.
While a receive on a closed channel returns immediately, you cannot close a closed channel.
We need a little coordination. We can do this with a sync.Mutex
and a bool
to indicate whether we've closed the channel or not. Let's create a struct to do this:
type Close struct {
C chan struct{}
l sync.Mutex
closed bool
}
func NewClose() *Close {
return &Close {
C: make(chan struct{}),
}
}
func (c *Close) Close() {
c.l.Lock()
if (!c.closed) {
c.closed=true
close(c.C)
}
c.l.Unlock()
}
Rewrite our gofunc and our main to use our new Close struct, and we're good to go:
https://play.golang.org/p/eH3djHu8EXW
The problem with concurrency is that you always need to be wondering what would happen if another 'thread' was anywhere else in the code.
答案4
得分: 1
你的问题是你希望在"完成"通道上发送一次,但却被多个监听器接收。你还需要考虑发送到done
通道的是由goroutine接收还是由main
函数接收。
我建议你将等待goroutine和done
通道分开。
import `sync`
// 这段代码将在两个函数完成之前等待,然后结束
func main {
var wait sync.WaitGroup
wait.Add(2)
go func() {
defer wait.Done()
}()
go g() {
defer wait.Done()
}()
wait.Wait()
}
现在,如何管理Done
。解决方案是使用sync.Cond
,并让每个goroutine运行自己的goroutine来等待Cond。以下是一个示例:
package main
import (
`fmt`
`sync`
`time`
)
// WaitForIt 封装了一个Cond和一个Mutex,提供了一个更简单的API:
// .WAIT() chan struct{} 返回一个通道,当WaitForIt完成时会发出信号。
// .Done() 表示WaitForIt完成。
type WaitForIt struct {
L *sync.Mutex
Cond *sync.Cond
}
func NewWaitForIt() *WaitForIt {
l := &sync.Mutex{}
c := sync.NewCond(l)
return &WaitForIt{ l, c }
}
// WAIT 返回一个通道,当Cond触发时会发出信号。
func (w *WaitForIt) WAIT() chan struct{} {
D := make(chan struct{})
go func() {
w.L.Lock()
defer w.L.Unlock()
w.Cond.Wait()
D <- struct{}{}
close(D)
}()
return D
}
// Done 表示Cond应该被触发。
func (w *WaitForIt) Done() {
w.Cond.Broadcast()
}
// doneFunc 使用一个通道来启动函数f,当函数应该停止时,通道会发出信号。
// 它还处理WaitGroup的同步。
func doneFunc(wait *sync.WaitGroup, waitForIt *WaitForIt, f func(DONE chan struct{})) {
defer wait.Done()
f(waitForIt.WAIT())
}
func main() {
// wait 将在main()函数级别协调所有的goroutine
// waitForIt 将协调所有的goroutine
wait := &sync.WaitGroup{}
// waitForIt 用于指示goroutine何时关闭
waitForIt := NewWaitForIt()
// goFunc 生成每个goroutine。只有3秒的goroutine会关闭所有的goroutine
goFunc := func(seconds int) func(chan struct{}) {
return func(DONE chan struct{}) {
// 这是每个goroutine的实际代码
// 它创建一个计时器,持续一定的秒数,
// 在计时器结束后打印秒数,
// 或者在DONE被触发时退出
timer := time.NewTicker(time.Duration(seconds) * time.Second)
defer timer.Stop()
for {
select {
case <- DONE:
return
case <- timer.C:
if (3==seconds) {
waitForIt.Done()
// 不要在这里关闭 - 我们将在DONE被触发时关闭
}
}
}
}
}
// 启动3个goroutine,每个都在等待关闭信号
for i:=1; i<=3; i++ {
wait.Add(1)
go doneFunc(wait, waitForIt, goFunc(i))
}
// 等待所有的goroutine完成,然后我们完成了
wait.Wait()
}
这是使用WaitForIt实现的示例:https://play.golang.org/p/llphW73G1xE
请注意,我不得不在WaitForIt.Done
中删除Lock()
调用。尽管文档说你可以持有锁,但它会阻塞第二个goroutine的完成。
英文:
Your problem is that you want a single send on the DONE channel to be received by multiple listeners. You also need to consider whether a send on the done
channel is received by your goroutines, or by your main
func.
I suggest you rather separate the waiting on go-routines and the done
channel.
import `sync`
// This code will wait for the two functions to complete before ending
func main {
var wait sync.WaitGroup
wait.Add(2)
go func() {
defer wait.Done()
}()
go g() {
defer wait.Done()
}()
wait.Wait()
}
Now, how to manage the Done. Well, the solution is to use a sync.Cond
and have each goroutine run its own goroutine to wait on the Cond. Here's an example:
package main
import (
`fmt`
`sync`
`time`
)
// WaitForIt wraps a Cond and a Mutex for a simpler API:
// .WAIT() chan struct{} will return a channel that will be
// signalled when the WaitForIt is done.
// .Done() will indicate that the WaitForIt is done.
type WaitForIt struct {
L *sync.Mutex
Cond *sync.Cond
}
func NewWaitForIt() *WaitForIt {
l := &sync.Mutex{}
c := sync.NewCond(l)
return &WaitForIt{ l, c }
}
// WAIT returns a chan that will be signalled when
// the Cond is triggered.
func (w *WaitForIt) WAIT() chan struct{} {
D := make(chan struct{})
go func() {
w.L.Lock()
defer w.L.Unlock()
w.Cond.Wait()
D <- struct{}{}
close(D)
}()
return D
}
// Done indicates that the Cond should be triggered.
func (w *WaitForIt) Done() {
w.Cond.Broadcast()
}
// doneFunc launches the func f with a chan that will be signalled when the
// func should stop. It also handles WaitGroup synchronization
func doneFunc(wait *sync.WaitGroup, waitForIt *WaitForIt, f func(DONE chan struct{})) {
defer wait.Done()
f(waitForIt.WAIT())
}
func main() {
// wait will coordinate all the goroutines at the level of main()
// between themselves the waitForIt will do the coordination
wait := &sync.WaitGroup{}
// waitForIt indicates to the goroutines when they should shut
waitForIt := NewWaitForIt()
// goFunc generates each goroutine. Only the 3-second goroutine will
// shutdown all goroutines
goFunc := func(seconds int) func(chan struct{}) {
return func(DONE chan struct{}) {
// this is the actual code of each goroutine
// it makes a ticker for a number of seconds,
// and prints the seconds after the ticker elapses,
// or exits if DONE is triggered
timer := time.NewTicker(time.Duration(seconds) * time.Second)
defer timer.Stop()
for {
select {
case <- DONE:
return
case <- timer.C:
if (3==seconds) {
waitForIt.Done()
// Don't shutdown here - we'll shutdown
// when our DONE is signalled
}
}
}
}
}
// launch 3 goroutines, each waiting on a shutdown signal
for i:=1; i<=3; i++ {
wait.Add(1)
go doneFunc(wait, waitForIt, goFunc(i))
}
// wait for all the goroutines to complete, and we're done
wait.Wait()
}
Here's your example implemented using WaitForIt: https://play.golang.org/p/llphW73G1xE
Note that I had to remove the Lock()
call in WaitForIt.Done
. Although the documentation says you're allowed to hold the lock, it was blocking your 2nd goroutine from completing.
答案5
得分: 0
package main
import (
"fmt"
"sync"
"time"
)
func func1(done chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
timer1 := time.NewTicker(1 * time.Second)
timer2 := time.NewTicker(2 * time.Second)
for {
select {
case <-timer1.C:
fmt.Println("定时器1函数1")
case <-timer2.C:
// 请求GC清理定时器timer1和timer2
// 因为goroutine即将返回
timer1.Stop()
timer2.Stop()
fmt.Println("定时器2函数1")
done <- struct{}{} // 通知另一个goroutine终止
fmt.Println("从函数1发送done")
return
case <-done:
// 请求GC清理定时器timer1和timer2
// 因为goroutine即将返回
timer1.Stop()
timer2.Stop()
fmt.Println("函数1完成")
return
}
}
}
func func2(done chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
timer3 := time.NewTicker(3 * time.Second)
for {
select {
case <-timer3.C:
// 请求GC清理定时器timer3
// 因为goroutine即将返回
timer3.Stop()
fmt.Println("定时器3函数2")
done <- struct{}{} // 通知另一个goroutine终止
fmt.Println("从函数2发送done")
return
case <-done:
// 请求GC清理定时器timer3
// 因为goroutine即将返回
timer3.Stop()
fmt.Println("函数2完成")
return
}
}
}
func main() {
// 用于goroutine之间的信号通信的通道
done := make(chan struct{})
// WaitGroup
wg := sync.WaitGroup{}
wg.Add(2)
// 启动func1的goroutine
go func1(done, &wg)
// 启动func2的goroutine
go func2(done, &wg)
fmt.Println("开始休眠")
// 等待goroutine完成
wg.Wait()
// 等待15秒钟
// 如果不需要,请删除下面的代码
time.Sleep(15 * time.Second)
fmt.Println("等待了15秒钟")
}
英文:
package main
import (
"fmt"
"sync"
"time"
)
func func1(done chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
timer1 := time.NewTicker(1 * time.Second)
timer2 := time.NewTicker(2 * time.Second)
for {
select {
case <-timer1.C:
fmt.Println("timer1 func 1")
case <-timer2.C:
// Ask GC to sweep the tickers timer1, timer2
// as goroutine should return
timer1.Stop()
timer2.Stop()
fmt.Println("timer2 func 1")
done <- struct{}{} // Signal the other goroutine to terminate
fmt.Println("sent done from func 1")
return
case <-done:
// Ask GC to sweep the tickers timer1, timer2
// as goroutine should return
timer1.Stop()
timer2.Stop()
fmt.Println("done func 1")
return
}
}
}
func func2(done chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
timer3 := time.NewTicker(3 * time.Second)
for {
select {
case <-timer3.C:
// Ask GC to sweep the tickers timer3
// as goroutine should return
timer3.Stop()
fmt.Println("timer3 func 2")
done <- struct{}{} // Signal the other goroutine to terminate
fmt.Println("sent done from func 2")
return
case <-done:
// Ask GC to sweep the tickers timer3
// as goroutine should return
timer3.Stop()
fmt.Println("done func 2")
return
}
}
}
func main() {
// Chan used for signalling between goroutines
done := make(chan struct{})
// WaitGroup
wg := sync.WaitGroup{}
wg.Add(2)
// Spawn the goroutine for func1
go func1(done, &wg)
// Spawn the goroutine for func2
go func2(done, &wg)
fmt.Println("starting sleep")
// Wait for the goroutines
wg.Wait()
// Wait for 15 seconds
// If not required, please remove
// the lines below
time.Sleep(15 * time.Second)
fmt.Println("waited 15 seconds")
}
答案6
得分: 0
你可以使用通道关闭模式在多个Go协程中等待。
package main
import (
"os"
"os/signal"
"sync"
"syscall"
)
type RunGroup struct {
sync.WaitGroup
}
// Run处理等待组状态
func (runGroup *RunGroup) Run(f func()) {
runGroup.Add(1)
go func() {
f()
runGroup.Done()
}()
}
func doStuff(done <-chan any, id int) {
println("Doing something", id)
<-done
println("DONE", id)
}
func main() {
// Done通道
done := make(chan any)
// 设置关闭监听器
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM)
signal.Notify(sigChan, syscall.SIGINT)
go func() {
rawSig := <-sigChan
sig := rawSig.String()
println("Caught signal, shutting down.", sig)
close(done)
}()
runGroup := RunGroup{}
// 做一些事情
runGroup.Run(func() {
doStuff(done, 1)
})
runGroup.Run(func() {
doStuff(done, 2)
})
runGroup.Run(func() {
doStuff(done, 3)
})
// 主线程等待中断
runGroup.Wait()
}
输出
go run ./main.go
Doing something 3
Doing something 2
Doing something 1
^CCaught signal, shutting down. interrupt
DONE 3
DONE 1
DONE 2
英文:
You could use channel closing pattern to wait within multiple go routines.
package main
import (
"os"
"os/signal"
"sync"
"syscall"
)
type RunGroup struct {
sync.WaitGroup
}
// Run handles wait group state
func (runGroup *RunGroup) Run(f func()) {
runGroup.Add(1)
go func() {
f()
runGroup.Done()
}()
}
func doStuff(done <-chan any, id int) {
println("Doing something", id)
<-done
println("DONE", id)
}
func main() {
// Done channel
done := make(chan any)
// Setup Shutdown listeners
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGTERM)
signal.Notify(sigChan, syscall.SIGINT)
go func() {
rawSig := <-sigChan
sig := rawSig.String()
println("Caught signal, shutting down.", sig)
close(done)
}()
runGroup := RunGroup{}
// Do some stuff
runGroup.Run(func () {
doStuff(done, 1)
})
runGroup.Run(func () {
doStuff(done, 2)
})
runGroup.Run(func () {
doStuff(done, 3)
})
// Wait mainthread until interrupt
runGroup.Wait()
}
Output
go run ./main.go
Doing something 3
Doing something 2
Doing something 1
^CCaught signal, shutting down. interrupt
DONE 3
DONE 1
DONE 2
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论