おんぶろぐ ver.2

おんぶろぐがインチキだらけなので改心しました

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

はい、そんな感じでした。