Concurrency in golang [Part-2]

Photo by Chinmay B on Unsplash

Concurrency in golang [Part-2]

In [Part -1] of this topic, we got an introduction to concurrent programming in Go. Moving on in this article we are going to discuss further.

There are the following challenges we face while working with concurrency in Go.

Challenges with Concurrency

  1. How to run things concurrently ( already addressed by goroutines)

  2. Coordinating tasks

  3. Shared Memory

Let's try to address those challenges.

And By using the sync package we will be able to address the other two challenges as well.

The Sync Package

sync.WaitGroups

A waitGroups wait for a collection of goroutines to finish.

You might have seen in the previous post we are adding time.sleep() so that don't exit without executing our goroutines. There I was assuming it will take 2 sec to execute all goroutines But what if we have more goroutines and executing those goroutines takes more than 2 sec? Well In that case our goroutines will remain unexecuted. We are going to fix that by using sync package from go.

So now we can replace time.sleep with the waitGroups

func calculateSquare(i int, wg *sync.WaitGroup){
    wg.Add(1)
    time.Sleep(1 * time.Second)
    fmt.Println(i * i)
    wg.Done();
}
func main() {
    wg := &sync.WaitGroups{}
    start := time.Now()
    for i := 1; i <= 1000; i++ {
        go calculateSquare(i, wg)
    }
    elapsed :=  time.Since(start)
    // time.Sleep(2 * time.Second)
    wg.Wait();
    fmt.Println("Function took", elapsed)
}

Here in the above code snippets you can we are initiating WaitGroups from sync package as wg . After initializing the calculateSquare function we added wg.Add(1) since we have one goroutine to be executed. After that, we have added wg.Done once the execution is complete. And in the main function, I have added wg.Wait() which will stop the main function from exiting without executing goroutines.

Mutex

The Mutual exclusion lock.

Mutex can be used to protect the portion of your code so that only one task or only the owner of the Mutex lock can access that code. We can use it to protect memory access. We lock the mutex access the memory and then unlock the mutex ensuring only one task can access the code at one time.

We can use a general mutex to solve this issue.

Here let's see an example.

package main
import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

var cache = map[int]Book{}
var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))

func main() {
    wg := &sync.WaitGroup{}
    m := &sync.RWMutex{} // &sync.Mutex{}

    for i := 0; i < 4; i++ {
        id := rnd.Intn(4) + 1
        wg.Add(2)
        go func(id int, wg *sync.WaitGroup, m *sync.RWMutex) {
            if b, ok := queryCache(id, m); ok {
                fmt.Println("from cache")
                fmt.Println(b)

            }
            wg.Done()
        }(id, wg, m)

        go func(id int, wg *sync.WaitGroup, m *sync.RWMutex) {
            if b, ok := queryDatabase(id, m); ok {
                fmt.Println("from database")
                fmt.Println(b)

            }
            wg.Done()
        }(id, wg, m)
        fmt.Printf("Book not found with id %v", id)
    }
    wg.Wait()
}

func queryCache(id int, m *sync.RWMutex) (Book, bool) {
    m.RLock()
    b, ok := cache[id] // reading the cache
    m.RUnlock()
    return b, ok
}

func queryDatabase(id int, m *sync.RWMutex) (Book, bool) {
    time.Sleep(100 * time.Millisecond)
    for _, b := range books {
        if b.ID == id {
            m.Lock()
            cache[id] = b // writing the cache
            m.Unlock()
            return b, true
        }
    }
    return Book{}, false
}

// way to find the if we are getting the race error
// on writing and reading the data at same time
// go run --race .

In general, we can use the sync.Mutex{} . But apart from that if you have a lot of read-write goroutines you can consider using sync.RWMutex{} as well. In the above code, we are querying data in queryCache . I have used sync.RWMutex{} .

Did you find this article valuable?

Support adityakmr by becoming a sponsor. Any amount is appreciated!