Home  >  Article  >  Backend Development  >  Using goroutine with sync.WaitGroup results in inconsistent results

Using goroutine with sync.WaitGroup results in inconsistent results

WBOY
WBOYforward
2024-02-09 10:30:101020browse

Using goroutine with sync.WaitGroup results in inconsistent results

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.

Question content

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?

Workaround

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!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete