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 ... }
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) } }
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) } }
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. factorialMap
geben? go
而已。不要小看这个go
,扯远了,当然大家知道这是go语言中开启一个协程的关键字即可。执行结果就是,控制台啥都没输出,这是因为主协程和子协程之间的执行关系,下面我们画图理解
从上图中我们可以发现,主协程执行的时间短(表现在比较短),子协程执行时间比较长(表现在比较长) 我们一定要记住,子协程是相对于当前的主协程来说的,如果主协程不存在了,那就没有子协程了
所以上面代码啥都没输出就是因为,主协程已经执行完了,但是子协程还没做完,那子协程都没做完,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) } }
当并发数比较小的时候,这个问题可能不会出现,一旦并发数变大,问题就立马出现了
图中的执行结果是并发map写入错误为什么会出现这个问题,我们假设100个人往一个篮子里放水果,很容易。但是100个人从一个篮子里拿水果,那就会出问题,首先,篮子里的水果不一定够100个,其二每个人都想先拿,必然会引起争抢。
针对上面的问题,我们引入全局锁的概念。这就有点像我们上厕所,100个人都想上厕所,但厕所只有1个,谁先抢到了谁先上,并且这个人还有给厕所上锁,防止其他人进来
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) } }
执行结果有0可能是数据类型存不下了导致的,这个大家不用关心
这样我们就解决了资源竞争的问题了。但其实还有一个问题,就是我们在主协程中还是必须手动等待,这要非常不好,那如果子协程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 denkenrrreeeWenn 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. 🎜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!