雑めも
-
errgroup.Group
を使っているけど、より原理的に書くとどうなるか整理 - 逆にいうと原理的に書くスタイルから、どうブラッシュアップされていくかみてみる
sync.WaitGroup
- Playgound
- 実行完了の制御として
sync.WaitGroup
を使う
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup // 実行完了の制御用
for i := range 5 { // 非同期N回実行
wg.Add(1) // increment
go func() {
defer wg.Done() // decrement
fmt.Printf("i is %s\n", i)
}()
}
wg.Wait() // wait
}
sync.WaitGroup
+ chan
を使った最大数制御
- Playground
- 自前で実行最大数を制御するためのchan を使う
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup //実行完了の制御
+ // 最大数の制御
+ const max = 2
+ sem := make(chan bool, max)
for i := range 5 { // 非同期N回実行
wg.Add(1) // increment
go func() {
+ sem <- true // channelの長さに空きがないと値を送れなくて、channel に入れられるまでwaitできる.
defer wg.Done() // decrement
+ defer func() { <-sem }() // 実行後に値を破棄する
fmt.Printf("i is %s\n", i)
}()
}
wg.Wait()
}
sync.WaitGroup
+ semaphore
を使った最大数制御
- Playgound
- 最大数の制御を自前のchanから、抽象化されたsemaphore に差し替え
package main
import (
+ "context"
"fmt"
"sync"
"time"
+ "golang.org/x/sync/semaphore"
)
func main() {
ctx := context.TODO()
var wg sync.WaitGroup //実行完了の制御
// 最大数の制御
const max = 2
- sem := make(chan bool, max)
+ sem := semaphore.NewWeighted(max)
for i := range 5 { // 非同期N回実行
wg.Add(1) // increment
go func() {
- sem <- true // channelの長さに空きがないと値を送れなくて、channel に入れられるまでwaitできる.
+ if err := sem.Acquire(ctx, 1); err != nil { // 1つ分の領域確保
+ fmt.Println("something wrong with sem.Acquire")
+ return
+ }
defer wg.Done() // decrement
- defer func() { <-sem }() // 実行後に値を破棄する
+ defer sem.Release(1) // 1つ分の領域解放
fmt.Printf("i is %s\n", i)
}()
}
wg.Wait()
}
errgroup.Group
- Playground
- 実行完了の制御、最大数の制御、エラーハンドリングをまとめて
errgroup.Group
にて行う
package main
import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
)
func main() {
ctx := context.TODO()
eg, _ := errgroup.WithContext(ctx)
eg.SetLimit(2) // 最大数の制御
for i := range 5 { // 非同期N回実行
eg.Go(func() error {
fmt.Printf("i is %s\n", i)
return nil
})
}
if err := eg.Wait(); err != nil {
panic(err)
}
}