Rabia Iftikhar
Rabia Iftikhar

Reputation: 29

Golang Goroutines - Fix Race Condition using Atomic Functions

I am a newbie in Golang and I am trying to understand the goroutines. Here is the piece of code which I got from https://www.golangprograms.com/goroutines.html.

package main

import (
    "fmt"
    "runtime"
    "sync"
    "sync/atomic"
)

var (
    counter int32          // counter is a variable incremented by all goroutines.
    wg      sync.WaitGroup // wg is used to wait for the program to finish.
)

func main() {
    wg.Add(3) // Add a count of two, one for each goroutine.

    go increment("Python")
    go increment("Java")
    go increment("Golang")

    wg.Wait() // Wait for the goroutines to finish.
    fmt.Println("Counter:", counter)

}

func increment(name string) {
    defer wg.Done() // Schedule the call to Done to tell main we are done.

    for range name {
        fmt.Println("name:", name)
        fmt.Println("Counter in range:", counter)
        atomic.AddInt32(&counter, 1)
        runtime.Gosched() // Yield the thread and be placed back in queue.
    }
}

Output:

name: Golang
Counter in range: 0
name: Java
Counter in range: 1
name: Golang
Counter in range: 2
name: Golang
Counter in range: 3
name: Java
Counter in range: 4
name: Golang
Counter in range: 5
name: Python
Counter in range: 6
name: Java
Counter in range: 7
name: Golang
Counter in range: 8
name: Java
Counter in range: 9
name: Golang
Counter in range: 10
name: Python
Counter in range: 11
name: Python
Counter in range: 12
name: Python
Counter in range: 13
name: Python
Counter in range: 14
name: Python
Counter in range: 15
Counter: 16

I am unable to understand that why the output is 16. Even we added only 3 goroutines. Shouldn't it be 3?

Can anyone please explain me?

Thanks.

Upvotes: 0

Views: 657

Answers (2)

cslrnr
cslrnr

Reputation: 747

You are range over the given input string, trange treat it as an array and iterates over it. See this example

package main

import (
    "fmt"
)

func main() {
    increment("test")
    increment("test_dev")
    increment("test_deploy")
    
}

func increment(name string) {
    for i, value := range name {
        fmt.Printf("name: %s index: %d, char: %c\n", name, i, value)

    }
}

Upvotes: 0

icza
icza

Reputation: 417652

I am unable to understand that why the output is 16. Even we added only 3 goroutines. Shouldn't it be 3?

Why should it be 3? It should be as many times atomic.AddInt32(&counter, 1) is called.

How many times is that? You launch 3 goroutines, and each has a loop. Incrementing is done inside the loops.

The loops:

for range name {}

The for range over a string iterates over the runes of the string. The names in your case are Golang, Java and Python. So the loop bodies will be executed as many times as many runes these strings have: 6 for Golang, 4 for Java and 6 for Python, that adds up to 16.

Upvotes: 3

Related Questions