In the Go language, goroutine can be used to execute tasks concurrently, and sync.WaitGroup is a synchronization mechanism used to wait for the completion of a group of goroutines. However, PHP Editor Banana found that in some cases, using goroutine with sync.WaitGroup may lead to inconsistent results. This problem usually occurs when multiple goroutines modify shared variables at the same time. Since the execution order of goroutines is uncertain, it may lead to inconsistency in the final results. In this article, we will explore the causes of this problem and provide some solutions to ensure result consistency between goroutines.
I am trying to use goroutine (in Go lang) to count the number of prime numbers less than an arbitrary integer i
.
For example, if i
is 100, the result should be 25
.
The following is my current implementation:
<code>package "main" import ( "fmt" "math" "sync" "time" ) var wg sync.WaitGroup func isprime(x int) bool { if x == 2 { return true } if x == 1 || x%2 == 0 { return false } var xi = float64(x) for i := 3; float64(i) < (math.Pow(xi, 0.5) + 1.0); i += 2.0 { if x%i == 0 { return false } } return true } func main() { fmt.Print("Till what number should I count primes? ") var i int fmt.Scan(&i) r := 0 pr := &r fmt.Println("Counting primes till ", i) start := time.Now() for x := 0; x <= i; x++ { wg.Add(1) go func(n int) { defer wg.Done() if isprime(n) { *pr += 1 } }(x) } wg.Wait() elapsed := time.Since(start).Seconds() fmt.Println("Counted", r, "primes") fmt.Println("took", elapsed, "seconds") } </code>
When I run this program, I get correct results for smaller i
values (up to about 1000)
But for larger i
values, the results are inconsistent and incorrect.
❯ ./main Till what number should I count primes? 10000 Counting primes till 10000 Counted 1228 primes took 0.006776541 seconds ❯ ./main Till what number should I count primes? 10000 Counting primes till 10000 Counted 1227 primes took 0.004183875 seconds ❯ ./main Till what number should I count primes? 1000000 Counting primes till 1000000 Counted 78254 primes took 0.441985921 seconds ❯ ./main Till what number should I count primes? 1000000 Counting primes till 1000000 Counted 78327 primes took 0.430042047 seconds
As the value of i
becomes larger, the result fluctuation increases. What causes this? Is there any way to make it consistent and correct?
You have a shared variable without proper synchronization. A race condition exists (*pr = 1
). Adding mutexes before and after the shared variable fixes it (mu.Lock(), mu.Unlock()
).
Code:
var wg sync.WaitGroup var mu sync.Mutex func main() { fmt.Print("Till what number should I count primes? ") var i int fmt.Scan(&i) r := 0 pr := &r fmt.Println("Counting primes till ", i) start := time.Now() for x := 0; x <= i; x++ { wg.Add(1) go func(n int) { defer wg.Done() if isprime(n) { mu.Lock() // <= lock *pr += 1 mu.Unlock() // <= unlock } }(x) } wg.Wait() elapsed := time.Since(start).Seconds() fmt.Println("Counted", r, "primes") fmt.Println("took", elapsed, "seconds") }
Output:
Till what number should I count primes? 1000000 Counting primes till 1000000 Counted 78498 primes took 0.6783484 seconds Till what number should I count primes? 1000000 Counting primes till 1000000 Counted 78498 primes took 0.5428273 seconds Till what number should I count primes? 1000000 Counting primes till 1000000 Counted 78498 primes took 0.5521617 seconds
The above is the detailed content of Using goroutine with sync.WaitGroup results in inconsistent results. For more information, please follow other related articles on the PHP Chinese website!