Heim >Backend-Entwicklung >Golang >Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache

青灯夜游
青灯夜游nach vorne
2023-02-17 14:40:203283Durchsuche

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache

Wir alle wissen, dass Thread-Sicherheit bei der gleichzeitigen Programmierung sehr wichtig ist. Als nächstes gehen wir von einem Szenario aus, um die unsichere Thread-Situation zu reproduzieren, und sprechen dann darüber, wie das Szenario in Go gelöst werden kann. Jetzt müssen wir ihre Fakultäten von 1 bis 100 finden und die Ergebnisse in eine Tabelle einfügen Die

1! = 1 = 1
2! = 1 * 2 = 2
3! = 1 * 2 * 3 = 6
4! = 1 * 2 * 3 * 4 = 24
5! = 1 * 2 * 3 * 4 * 5 = 120
...
{
    1: 1
    2: 2
    3: 6
    4: 24
    5: 120
    ...
}

Code-Implementierung in der Karte

var factorialMap = make(map[int]int)

func Factorial(n int) {
    result := 1
    for i := 1; i <= n; i++ {
        result *= i
    }
    factorialMap[n] = result
}

func main() {
    for i := 1; i < 10; i++ {
        Factorial(i)
    }
    for k, v := range factorialMap {
        fmt.Printf("%d 的阶乘是%d\n", k, v)
    }
}

Das obige Codeausführungsergebnis ist eigentlich kein Problem. Warum liegt eine Out-of-Order-Situation vor? Da dies die Karte in der Go-Sprache ist, ist sie nach unserem Verständnis tatsächlich nicht in Ordnung, aber leider ist Golangs Karte nicht so. Bei der obigen Ausführung gibt es kein Problem. Aufmerksame Schüler haben vielleicht herausgefunden, dass diese Version des Codes keine Parallelität verwendet, oder? Okay, lasst uns die

Parallelitätsimplementierung

var factorialMap = make(map[int]int)

func Factorial(n int) {
    result := 1
    for i := 1; i <= n; i++ {
        result *= i
    }
    factorialMap[n] = result
}

func main() {
    for i := 1; i < 10; i++ {
        go Factorial(i)
    }
    for k, v := range factorialMap {
        fmt.Printf("%d 的阶乘是%d\n", k, v)
    }
}
Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache

weiter verbessern. Wir können feststellen, dass die gleichzeitige Version einfach einen go vor dem Aufruf hinzufügt, um die Fakultätsfunktion zu berechnen. Unterschätzen Sie dieses go nicht, es ist zu weit hergeholt. Natürlich weiß jeder, dass dies das Schlüsselwort ist, um eine Coroutine in der Go-Sprache zu starten.

Das Ausführungsergebnis ist, dass nichts an die Konsole ausgegeben wird. Dies liegt an der Ausführungsbeziehung zwischen der Haupt-Coroutine und der Unter-Coroutine. Lassen Sie uns unten ein Bild zeichnen, um es zu verstehen

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-SpracheAus dem Bild oben können wir erkennen, dass die Ausführungszeit des Die Hauptkoroutine ist kurz (sie ist relativ kurz), die Ausführungszeit der Unterkoroutine ist relativ lang (wird als relativ lang angezeigt). Wir müssen bedenken, dass die Unterkoroutine relativ zur aktuellen Hauptkoroutine ist. Wenn die Hauptkoroutine nicht mehr vorhanden ist, gibt der obige Code nichts aus, da die Hauptkoroutine ausgeführt wurde. aber die Sub-Coroutine wurde nicht abgeschlossen. Kann es etwas in factorialMap geben?

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-SpracheHaupt- und andere Unterkoroutinengo而已。不要小看这个go,扯远了,当然大家知道这是go语言中开启一个协程的关键字即可。

执行结果就是,控制台啥都没输出,这是因为主协程和子协程之间的执行关系,下面我们画图理解

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache从上图中我们可以发现,主协程执行的时间短(表现在比较短),子协程执行时间比较长(表现在比较长) 我们一定要记住,子协程是相对于当前的主协程来说的,如果主协程不存在了,那就没有子协程了

所以上面代码啥都没输出就是因为,主协程已经执行完了,但是子协程还没做完,那子协程都没做完,factorialMap中能有东西吗?

主等子

这就引出我们第一个问题,主协程如何等待子协程执行完再退出程序。我们现在用一个最简单,最容易想到的做法

var factorialMap = make(map[int]int)

func Factorial(n int) {
    result := 1
    for i := 1; i <= n; i++ {
        result *= i
    }
    factorialMap[n] = result
}

func main() {
    for i := 1; i < 100; i++ {
        go Factorial(i)
    }
    time.Sleep(time.Second * 3)
    for k, v := range factorialMap {
        fmt.Printf("%d 的阶乘是%d\n", k, v)
    }
}

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache当并发数比较小的时候,这个问题可能不会出现,一旦并发数变大,问题就立马出现了

图中的执行结果是并发map写入错误为什么会出现这个问题,我们假设100个人往一个篮子里放水果,很容易。但是100个人从一个篮子里拿水果,那就会出问题,首先,篮子里的水果不一定够100个,其二每个人都想先拿,必然会引起争抢。

问题一优化

针对上面的问题,我们引入全局锁的概念。这就有点像我们上厕所,100个人都想上厕所,但厕所只有1个,谁先抢到了谁先上,并且这个人还有给厕所上锁,防止其他人进来

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache

var factorialMap = make(map[int]int)
var lock sync.Mutex

func Factorial(n int) {
    result := 1
    for i := 1; i <= n; i++ {
            result *= i
    }
    // defer 不好理解
    // defer func(){
    // 	lock.Unlock() // 执行完解锁
    // }()
    lock.Lock() // 执行时上锁
    factorialMap[n] = result
    lock.Unlock() // 执行后解锁
}

func main() {
    for i := 1; i < 100; i++ {
        go Factorial(i)
    }
    time.Sleep(time.Second * 3)
    for k, v := range factorialMap {
        fmt.Printf("%d 的阶乘是%d\n", k, v)
    }
}

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache执行结果有0可能是数据类型存不下了导致的,这个大家不用关心

Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache这样我们就解决了资源竞争的问题了。但其实还有一个问题,就是我们在主协程中还是必须手动等待,这要非常不好,那如果子协程3秒内解决不了怎么办?

问题二优化

这个问题是我们不想在主协程中手动等待子协程,换句话说是我们不想直接在代码中写明要等待多长时间

这里我们就引入了WaitGroup

var factorialMap = make(map[int]int)
var lock sync.Mutex
var wg sync.WaitGroup

func Factorial(n int) {
    result := 1
    for i := 1; i <= n; i++ {
        result *= i
    }
    lock.Lock() // 执行时上锁
    factorialMap[n] = result
    lock.Unlock() // 执行后解锁
    wg.Done()
}

func main() {
    for i := 1; i < 100; i++ {
        wg.Add(1)
        go Factorial(i)
    }
    wg.Wait()
    for k, v := range factorialMap {
        fmt.Printf("%d 的阶乘是%d\n", k, v)
    }
}

WaitGroup的内部原理大家自己细扣,我这就不讲了 总结来说就是WaitGroup

Dies führt zu unserer ersten Frage: Wie wartet die Hauptkoroutine darauf, dass die Ausführung der Unterkoroutine abgeschlossen ist, bevor sie das Programm verlässt? Wir verwenden jetzt die einfachste und einfachste Art, es zu denkenrrreee image .pngWenn die Anzahl der Parallelität relativ gering ist, tritt dieses Problem möglicherweise nicht auf. Sobald die Anzahl der Parallelität groß wird, tritt das Problem sofort auf. Das Ausführungsergebnis im Bild ist

Concurrent Map Schreibfehler 🎜Warum tritt dieses Problem auf? Nehmen wir an, dass 100 Personen Früchte in einen Korb legen. Aber wenn 100 Leute Früchte aus einem Korb nehmen, wird es erstens Probleme geben, weil dann vielleicht nicht genug Früchte im Korb sind und zweitens jeder es zuerst nehmen will, was unweigerlich zu Konkurrenz führt. 🎜

🎜Problem 1 Optimierung🎜🎜🎜Angesichts des oben genannten Problems führen wir das Konzept der globalen Sperre ein. Das ist ein bisschen so, als würden 100 Leute auf die Toilette gehen, aber es gibt nur eine Toilette, die zuerst geht, und diese Person schließt auch die Toilette ab, um zu verhindern, dass andere hineinkommen 🎜Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache🎜rrreee🎜Das Ausführungsergebnis ist 0, Dies kann daran liegen, dass der Datentyp nicht gespeichert werden kann. Ja, Sie müssen sich darüber keine Sorgen machen🎜🎜Ein Artikel über das Problem der Ressourcenkonkurrenz in der Go-SpracheAuf diese Weise lösen wir das Problem der Ressourcenkonkurrenz. Aber es gibt tatsächlich ein anderes Problem: Wir müssen in der Haupt-Coroutine immer noch manuell warten, was sehr schlimm ist. Was ist, wenn die Unter-Coroutine nicht innerhalb von 3 Sekunden gelöst werden kann? 🎜

🎜Problem 2 Optimierung🎜🎜🎜Dieses Problem besteht darin, dass wir nicht manuell auf die Unterkoroutine in der Hauptkoroutine warten möchten Ich möchte direkt in den Code schreiben, um zu warten. Wie lange🎜🎜Hier haben wir WaitGroup eingeführt🎜rrreee🎜Sie können die internen Prinzipien von WaitGroup im Detail studieren, ich werde jetzt nicht darauf eingehen Zusammenfassend ist WaitGroup ein Korb. Jedes Mal, wenn eine Coroutine geöffnet wird, wird dem Korb ein Bezeichner (Funktion hinzufügen) hinzugefügt. Fertig (Funktion) und überprüfen Sie abschließend den Warenkorb. Wenn er leer ist, bedeutet dies, dass die Coroutine ausgeführt wurde (Wartefunktion)🎜🎜[Empfohlenes Lernen: 🎜Video-Tutorial besuchen🎜]🎜

Das obige ist der detaillierte Inhalt vonEin Artikel über das Problem der Ressourcenkonkurrenz in der Go-Sprache. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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