Golang の並列化パターン - Doneチャネル
課題
goroutine 開始後に処理を中断したいケースがあるが、goroutine 自体に中断機能が無い
解決
チャネル経由で中断リクエストを連携させることで中断機能を実現する
実践
下記は 100の仕事 (work) を 1秒に1個処理する労働者(worker) を表現したコード
中断リクエストを利用して 5秒後に処理キャンセルを行っている
package main import ( "fmt" "time" ) type done struct{} type work struct{} func main() { // 終了通知のチャネルを用意します doneChan := make(chan done) // 仕事が100個入った仕事のチャネルを用意します workChan := createChanWithWorks(100) // 1秒に1個ずつ仕事を終わらせられる労働者を用意します go runWorker(doneChan, workChan, 1*time.Second) // 5秒後にキャンセルします time.Sleep(5 * time.Second) close(doneChan) // `doneChan <- done{}` でも動作するが、`close(doneChan)` 形式の方がチャネルレベルで状態更新が入る分、例外系の処理フロー(送受信間)に強い // main goroutineが先に終わらないよう 3秒待機します time.Sleep(3 * time.Second) } func createChanWithWorks(numWork int) <-chan work { workChan := make(chan work, numWork) // 第2引数はチャネルのサイズ。最初に100の仕事が入る様に拡張 for i := 0; i < numWork; i++ { workChan <- work{} } fmt.Printf("Create %d works \n", numWork) return workChan } func runWorker(doneChan <-chan done, workChan <-chan work, processTime time.Duration) { for { select { case <-workChan: fmt.Println("Working...") time.Sleep(processTime) case <-doneChan: fmt.Println("Done") return } } }
実行すると 約5秒後に処理がキャンセルされるのが確認できると思います。
Create 100 works Working... Working... Working... Working... Working... Done Process finished with exit code 0
はい、そんな感じでした。