Heim >Backend-Entwicklung >Golang >Analysieren Sie Golangs WaitGroup-Falle und lösen Sie das Problem

Analysieren Sie Golangs WaitGroup-Falle und lösen Sie das Problem

藏色散人
藏色散人nach vorne
2021-09-14 15:57:022016Durchsuche

Dieser Artikel wird Ihnen durch die Kolumne „Go-Sprach-Tutorial“ über Golangs WaitGroup-Falle vorgestellt. Ich hoffe, dass er Freunden in Not hilfreich sein wird!

Golangs WaitGroup-Fallesync.WaitGroup ist eine sehr häufige Datenstruktur in einer gleichzeitigen Umgebung, die verwendet wird, um beim Schreiben von Code auf das Ende aller Coroutinen zu warten ein Beispiel, und es besteht keine Notwendigkeit, sich mit seiner Verwendung zu befassen. Vor ein paar Tagen habe ich mich gefragt, ob ich die Funktion Add() in einer Coroutine ausführen kann. Hier ist eine Einführung.

Die Falle liegt in der Aufrufreihenfolge der drei Funktionen von WaitGroup. Sehen wir uns zunächst die Funktionen der drei Funktionen an: sync.WaitGroup是并发环境中,一个相当常用的数据结构,用来等待所有协程的结束,在写代码的时候都是按着例子的样子写的,也没用深究过它的使用。前几日想着能不能在协程中执行Add()函数,答案是不能,这里介绍下。

陷阱在WaitGroup的3个函数的调用顺序上。先回顾下3个函数的功能:

  1. Add(delta int):给计数器增加delta,比如启动1个协程就增加1。
  2. Done():协程退出前执行,把计数器减1。
  3. Wait():阻塞等待计数器为0。

考一考

下面的程序是创建了协程father,然后father协程创建了10个子协程,main函数等待所有协程结束后退出,看看下面代码有没有什么问题?

package main

import (
    "fmt"
    "sync"
)

func father(wg *sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()

    fmt.Printf("father\n")
    for i := 0; i < 10; i++ {
        go child(wg, i)
    }
}

func child(wg *sync.WaitGroup, id int) {
    wg.Add(1)
    defer wg.Done()

    fmt.Printf("child [%d]\n", id)
}

func main() {
    var wg sync.WaitGroup
    go father(&wg)

    wg.Wait()
    log.Printf("main: father and all chindren exit")
}

发现问题了吗?如果没有看下面的运行结果:main函数在子协程结束前就开始结束了。

father
main: father and all chindren exit
child [9]
child [0]
child [4]
child [7]
child [8]

陷阱分析

产生以上问题的原因在于,创建协程后在协程内才执行Add()函数,而此时Wait()函数可能已经在执行,甚至Wait()函数在所有Add()执行前就执行了,Wait()

  1. Add(delta int): Delta zum Zähler hinzufügen, zum Beispiel um 1 erhöhen, wenn eine Coroutine gestartet wird.
  2. Done(): Wird ausgeführt, bevor die Coroutine beendet wird, wodurch der Zähler um 1 verringert wird.
  3. Wait(): Der blockierende Wartezähler ist 0.
Machen Sie einen Test

Das folgende Programm erstellt den Coroutine-Vater und dann erstellt die Vater-Coroutine 10 Unter-Coroutinen. Die Hauptfunktion wartet, bis alle Coroutinen fertig sind, und wird dann beendet unten. Kein Problem?

package main

import (
    "fmt"
    "sync"
)

func father(wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("father\n")
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go child(wg, i)
    }
}

func child(wg *sync.WaitGroup, id int) {
    defer wg.Done()
    fmt.Printf("child [%d]\n", id)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go father(&wg)

    wg.Wait()
    fmt.Println("main: father and all chindren exit")
}
Haben Sie das Problem gefunden? Wenn Sie die folgenden Ausführungsergebnisse nicht sehen: Die Hauptfunktion beginnt zu enden, bevor die Unter-Coroutine endet.
father
child [9]
child [7]
child [8]
child [1]
child [4]
child [5]
child [2]
child [6]
child [0]
child [3]
main: father and all chindren exit
Trap-AnalyseDer Grund für das obige Problem ist, dass die Funktion Add() innerhalb der Coroutine ausgeführt wird, nachdem die Coroutine erstellt wurde, und zu diesem Zeitpunkt die Funktion Wait()-Funktion <p>Vielleicht</p> wird bereits ausgeführt, oder sogar die <code>Wait()-Funktion wird ausgeführt, bevor alle Add() ausgeführt werden, und zwar sofort, wenn Wait () wird ausgeführt. Es wird sichergestellt, dass der Zähler von WaitGroup 0 ist, Wait endet und das Hauptprogramm wird beendet. Infolgedessen wurden nicht alle Unter-Coroutinen vollständig beendet und die Hauptfunktion endet.

Der richtige Ansatz
🎜🎜Die Add-Funktion muss ausgeführt werden, bevor die Wait-Funktion ausgeführt wird🎜 Dies wird in der Dokumentation der Add-Funktion gefordert: 🎜Beachten Sie, dass Aufrufe mit einem positiven Delta erfolgen müssen, wenn der Zähler Null ist vor einem Warten.🎜. 🎜🎜Wie kann sichergestellt werden, dass die Add-Funktion vor der Wait-Funktion ausgeführt werden muss? Bei Coroutinen können wir nicht vorhersagen, ob die Ausführungszeit des Codes in der Coroutine früher ist als die Ausführungszeit der Wait-Funktion. Wir können dies jedoch sicherstellen, indem wir die Add-Funktion ausführen, bevor wir die Coroutine zuweisen, und dann ausführen Wartefunktion. 🎜🎜Das Folgende ist das geänderte Programm und die Ausgabeergebnisse. 🎜rrreeerrree🎜

Das obige ist der detaillierte Inhalt vonAnalysieren Sie Golangs WaitGroup-Falle und lösen Sie das Problem. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen