技術向上

プログラミングの学び、気になるテクノロジーやビジネストレンドを発信

semaphore(同時実行数制限)【Go】

goroutineの同時実行数を制限することができるpackageです。

golang.orgから提供されていますが、goのmainツリーとは別のプロジェクトになります。
そのため、使用するには、下記コマンドを実行してインストールする必要があります。

$ go get golang.org/x/sync/semaphore


下記例は、goroutineの同時実行を最大2つまでに制限します。
また、contextの生成が必須になります。

var sn = semaphore.NewWeighted(2)    // goroutineの同時実行を最大2つまでに制限

func longProcess(ctx context.Context) {
    if err := sn.Acquire(ctx, 1); err != nil {    // 指定した同時実行数制限snから1つ実行権限を取得。上限に達していて取得できない場合は、取得でき次第、実行を開始
        log.Fatalln(err)
    }
    defer sn.Release(1)    // 最後にsn実行権限枠を1つ空ける

    fmt.Println("start..")
    time.Sleep(1 * time.Second)
    fmt.Println("done")
}

func main() {
    ctx := context.TODO()    // contextの機能は今回利用しないため、TODOとする
    go longProcess(ctx)
    go longProcess(ctx)
    go longProcess(ctx)
    time.Sleep(4 * time.Second)    // main関数が事前に終了してしまわないようsleep
}


上記例では、同時実行できないものについて、
semaphore.NewWeighted(i int).Acquire(ctx context, j int) error を用いて、
実行権限の枠が取得でき次第、実行を開始させました。
下記例では、同時実行できないものについては、処理を実行させないようにすることができます。

var sn = semaphore.NewWeighted(2)

func longProcess() {
    if !sn.TryAcquire(1) {    // 実行権限が取得できるかどうか、bool値を返す
        fmt.Println("could not acquire")
        return    // 上限に達していて、実行権限が取得できない場合は処理を終了させる
    }
    defer sn.Release(1)
    fmt.Println("start..")
    time.Sleep(1 * time.Second)
    fmt.Println("done")
}

func main() {
    go longProcess()
    go longProcess()
    go longProcess()
    time.Sleep(3 * time.Second)
    go longProcess()
    time.Sleep(2 * time.Second)
}

semaphore.NewWeighted(i int).TryAcquire(j int) boolの実行に際しては、
context.Contextは必要ありません。