Skip to content

Instantly share code, notes, and snippets.

@bamchoh
Created March 2, 2024 01:48
Show Gist options
  • Save bamchoh/639c2c42d2422a2310b6bf50ee8cd2ef to your computer and use it in GitHub Desktop.
Save bamchoh/639c2c42d2422a2310b6bf50ee8cd2ef to your computer and use it in GitHub Desktop.
CTRL+Cを押してアプリを終了するときにwaitGroupでのWait()で無限待ちにならないようにする方法
/*
単純に main関数の終わりで シグナルを待つようにすれば無限待ちにならない
以前は main関数の終わりでは wg.Wait()で待つようにしていて
シグナルを待つ処理をゴルーチンでやってシグナルが来たらcancel()を読んで
ほかのゴルーチンに通知する方式でやっていた。
この方式だと、いろんなところに ctx.Done() とのselect を書く必要があり
書き忘れが発生するとタスクをキルするしかなくなっていた
今回の方法だと書き忘れがあってもアプリがちゃんと終了してくれるので
コードもすっきりするし期待した動作もしてくれるしで両方ハッピー
*/
package main
import (
"context"
"fmt"
"math/rand"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
func fn(ctx context.Context, cancel context.CancelFunc, wg *sync.WaitGroup, name string, n int) {
defer wg.Done()
rnd := rand.Intn((n + 1) * 100)
for {
select {
case <-ctx.Done():
fmt.Printf("%s(%d) : another func timedout\n", name, n)
return
case <-time.After(time.Duration(rnd) * time.Millisecond):
fmt.Printf("%s(%d) : timeout after %d ms\n", name, n, rnd)
cancel()
return
}
}
}
func loop(i int) {
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go fn(ctx, cancel, &wg, "func1", i)
wg.Add(1)
go fn(ctx, cancel, &wg, "func2", i)
wg.Wait()
}
func main() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
for i := 0; ; i++ {
loop(i) // 複雑な並行処理
time.Sleep(1 * time.Second)
}
}()
fmt.Println("awaiting signal")
<-sigs
fmt.Println("exiting")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment