We all know that thread safety is very important in concurrent programming. Next, we will assume a scenario to reproduce the thread insecurity situation, and then talk about how to solve the
We now need to 1~ 100 find their factorial and put the result into a map
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) } }
The above code execution result is actually no problem, why Will there be disorder? Because this is the map in the Go language, it is actually out of order. According to our understanding, the first stored, first out, but sorry, Golang's map is not like this. There is no problem with the above execution. Careful students may have discovered that this version of the code does not use concurrency, right? Okay, let’s continue to improve
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) } }
We can find that the concurrent version adds a ## in front of the call to calculate the factorial function. #go Just. Don’t underestimate this
go, it’s too far-fetched. Of course, everyone knows that this is the keyword to start a coroutine in the go language.
From the above picture we It can be found that the execution time of the main coroutine is short (shown as relatively short), and the execution time of the sub-coroutine is relatively long (shown as relatively long) We must remember that the sub-coroutine is relative to the current main coroutine. If the main coroutine no longer exists, then there will be no sub-coroutine
So the above code outputs nothing. Because the main coroutine has been executed, but the sub-coroutine has not been completed. If the sub-coroutine has not been completed, can there be anything infactorialMap?
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) } }
When the number of concurrencies is relatively small, this problem may not occur. Once the number of concurrencies becomes larger, the problem will appear immediately
The execution result in the picture isConcurrent map writing errorWhy does this problem occur? We assume that 100 people put fruit into a basket, which is easy. But if 100 people take fruits from a basket, there will be problems. First, there may not be enough fruits in the basket. Second, everyone wants to take it first, which will inevitably cause competition.
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) } }
The execution result of 0 may be caused by the data type not being able to be saved. You don’t need to worry about this
This way we will solve the problem of resource competition. But there is actually another problem, that is, we still have to wait manually in the main coroutine, which is very bad. What if the sub-coroutine cannot solve it within 3 seconds?
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 is a basket. Every time a coroutine is opened, an identifier (Add function) is added to the basket. Every time a coroutine is executed, an identifier (Done) is subtracted from the basket. function), and finally check the basket. If it is empty, it means that the coroutine has been executed (Wait function)
go video tutorial]
The above is the detailed content of An article to talk about the resource competition problem in Go language. For more information, please follow other related articles on the PHP Chinese website!