Golangはsyncを利用する.WaitGroupによるコヒーレント同期の詳細
3022 ワード
きょうどうどうき
実際のプロジェクト開発では、コンカレント同期が必要なシーンが頻繁に発生します.主コンカレントで作成されたコンカレント実行が完了してから、主コンカレントを終了する方法を尋ねる人がよく見られます.たとえば、次のコードでは、100コンカレントで同時印刷を実現する例を示します.
package main
import (
"fmt"
)
func main() {
for i := 0; i < 100 ; i++{
go func(i int) {
fmt.Println("Goroutine ",i)
}(i)
}
}
以上のコードを実行すると、出力が見えない可能性が高いか、一部のコヒーレンスのみが実行される可能性があります.この100のコヒーレンスがまだ実行されていない可能性があります.または、一部のコヒーレンスが実行されていない可能性があります.メインコヒーレンスが終了すると、他のすべてのコヒーレンスが終了します.解決策はmain関数の最後にsleep()待機を追加することです.
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 100 ; i++{
go func(i int) {
fmt.Println("Goroutine ",i)
}(i)
}
time.Sleep(time.Second * 1) // 1 ,
}
メインスレッドはgoroutineの実行が完了するのを待つために、プログラムの最後にtimeを使用せざるを得ない.Sleep()はしばらく眠って、他のスレッドが十分に実行されるのを待っています.簡単なコードの場合、複数のforループは1秒以内に実行できます.time.Sleep()も所望の効果を達成できます.しかし、実際の生活の多くのシーンでは、1秒が足りず、forループ内のコードの実行時間の長さを予知できないことが多い.このときtimeは使用できません.Sleep()が待機操作を完了しました.これは完璧な解決策ではありません.この2つの協力に複雑な操作が含まれている場合、時間がかかる可能性があります.睡眠時間がどのくらいかかるかは判断できません.もちろん、パイプで同期することができます.
package main
import (
"fmt"
)
func main() {
ch := make(chan struct{})
count := 100 // count
for i := 0; i < 100 ; i++{
go func(i int) {
fmt.Println("Goroutine ",i)
ch
上の解決策は比較的完璧な方案で、まずパイプを使うことが私たちの目的を達成することができて、その上目的を達成することができるだけではなくて、また非常に完璧に目的を達成することができます.しかし、パイプはここでは単純な同期処理として使用されるだけでなく、ここでパイプを使用するのは実際には適切ではないため、少し役に立たないように見えます.さらに、1万、10万、さらに多くのforサイクルがあると仮定して、同じサイズのパイプを申請し、メモリにも大きなコストがかかります.Goはsyncを使用するより簡単な方法を提供した.WaitGroup.WaitGroupはその名の通り、一連の操作が完了するのを待つために使用されます.WaitGroupの内部には、未完了の操作数を記録するカウンタが実装されており、カウントを追加するための3つの方法が提供されています.Done()は、操作終了時に呼び出し、カウントを1つ減らすために使用されます.Wait()は、すべての操作が終了するのを待つために使用されます.すなわち、カウントが0になり、カウントが0でない場合に待機し、カウントが0の場合にすぐに戻ります.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(100) // , 2
for i := 0; i < 100 ; i++{
go func(i int) {
fmt.Println("Goroutine ",i)
wg.Done() // ,
}(i)
}
wg.Wait() // , 0
}
可視用sync.WaitGroupは最も簡単な方法です.
注意事項
1.カウンタは負の値ではありません.Add()を使用してwgに負の値を設定することはできません.そうしないと、コードがエラーになります.
panic: sync: negative WaitGroup counter
goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc042008230, 0xffffffffffffff9c)
D:/Go/src/sync/waitgroup.go:25 +0x1d0
main.main()
D:/code/go/src/test-src/2-Package/sync/waitgroup/main.go:10 +0x54
同様にDone()を使ってもカウンタを負数にしないように注意しましょう.
2.WaitGroupオブジェクトは参照タイプではありませんWaitGroupオブジェクトは参照タイプではありません.関数を介して値を渡すときにアドレスを使用する必要があります.
func main() {
wg := sync.WaitGroup{}
wg.Add(100)
for i := 0; i < 100; i++ {
go f(i, &wg)
}
wg.Wait()
}
// ,
func f(i int, wg *sync.WaitGroup) {
fmt.Println(i)
wg.Done()
}
学び合う.共に進歩する.