こぼれネット

goroutineから、サブgoroutineを消滅させる方法

これは、結構オーソドックスな手法で、いろいろなところに使われています。

/*
	お題: personが終了したらsubPerson1も subPerson2も強制的に終了させるには?
*/

package main

import (
	"fmt"
	"sync"
	"time"
)

func subPerson1(i int, wg *sync.WaitGroup, ch chan struct{}) {
	defer wg.Done()

	<-ch // close(ch)が発行されるまでロック (普通はselect待ちにする)
	fmt.Println("		End of sub_person1(", i, ")")

}

func subPerson2(i int, wg *sync.WaitGroup, ch chan struct{}) {
	defer wg.Done()
	<-ch // close(ch)が発行されるまでロック (普通はselect待ちにする)
	fmt.Println("		End of sub_person2(", i, ")")
}

func person(i int, wg *sync.WaitGroup) {
	fmt.Println("	Start... person(", i, ")")
	defer wg.Done()

	subWG := sync.WaitGroup{}

	subWG.Add(2)
	ch := make(chan struct{})
	go subPerson1(i, &subWG, ch)
	go subPerson2(i, &subWG, ch)

	time.Sleep(10 * time.Second)
	close(ch)

	subWG.Wait()

	fmt.Println("	End... person(", i, ")")
}

func main() {
	fmt.Println("start... main()")
	wg := sync.WaitGroup{}
	for i := 0; i < 5; i++ {
		wg.Add(1) // goルーチンを実行する関数分だけAddする

		go person(i, &wg)
	}

	wg.Wait()
	fmt.Println("end of ... main()")
}

出力結果はこんな感じです。
ちょっと変な感じがしますが、多分Printlnを記載している時間タイミングに因るものだろう、と思います。

$ go run main.go
start... main()
        Start... person( 4 )
        Start... person( 2 )
        Start... person( 3 )
        Start... person( 0 )
        Start... person( 1 )
                End of sub_person2( 2 )
                End of sub_person2( 0 )
                End of sub_person1( 0 )
        End... person( 0 )
                End of sub_person2( 4 )
                End of sub_person1( 4 )
        End... person( 4 )
                End of sub_person2( 3 )
                End of sub_person1( 3 )
        End... person( 3 )
                End of sub_person1( 2 )
        End... person( 2 )
                End of sub_person2( 1 )
                End of sub_person1( 1 )
        End... person( 1 )
end of ... main()
モバイルバージョンを終了