Search code examples
gogoroutine

How to print N integers using odd and even thread


I am trying to print N numbers from 2 go routines:

go routine odd(): this can only print odd numbers

go routine even(): this can only print even numbers

The output should be: 1 2 3 4 5 6 7 8 9 10

I am trying to solve this problem using sync.WaitGroup. I have following queries:

Q1. Which concurrency mechanism best suited for this problem? channel, waitgroup, mutex, etc? It would be ideal if you could provide a working code for the same.

Q2. Why I am not able to print the sequence correctly through below code? I am doing something wrong which I am not able to rectify. please help in rectifying.

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup
var wgO sync.WaitGroup
var wgE sync.WaitGroup

func even() {
    defer wg.Done()

    for i := 2; i <= 10; i += 2 {
        wgE.Add(1)
        wgO.Wait()
        fmt.Println(i)
        wgE.Done()
    }
}
func odd() {
    defer wg.Done()

    for i := 1; i <= 10; i += 2 {
        wgO.Add(1)
        fmt.Println(i)
        wgO.Done()
        wgE.Wait()
    }
}
func main() {
    wg.Add(2)
    go even()
    go odd()
    wg.Wait()
}

Solution

  • Q1: Which concurrency mechanism best suited for this problem?

    A1: None. Your problem of printing sequential numbers is not a concurrent one. So even if you implemented a solution that uses Go's concurrent mechanisms (either with channels or mutexs), it wouldn't/couldn't actually run concurrently, since what you want is sequentially printing your numbers. Running concurrently would print numbers is a non-deterministic order.

    Q2: Why I am not able to print the sequence correctly through below code?

    A2: Your code prints out of order because once go routines have been triggered, you have no way of knowing the order in which they are executed. So the code:

    ...
    go even()
    go odd()
    ...
    

    doesn't even guarantee that the loop inside your even function will start before the loop inside your odd function, even thou the even function is called before.

    A2.1: Your code can panic sometimes WaitGroup is reused before previous Wait has returned, because it's possible for wgO.Done() inside odd function to be called before wgO.Wait() is called inside the even function.


    The following is a very silly implementation using sync.WaitGroup that works, and illustrates how the solution to your problem has to break concurrency to work. In order to print the numbers in order, I have to wait the completion of each go routine...

    func main() {
        var wg sync.WaitGroup
        
        for i := 0; i <= 10; i++ {
            if i%2 == 0 {
                wg.Add(1)
                go func(i int) {
                    defer wg.Done()
                    fmt.Println(i)
                }(i)
                wg.Wait()
            } else {
                wg.Add(1)
                go func(i int) {
                    defer wg.Done()
                    fmt.Println(i)
                }(i)
                wg.Wait()
            }
        }
    }
    

    I wouldn't get too caught up in this problem as it's not a good example for leaning Go's concurrency mechanism.

    There is a great course on cooursera for learning Go's concurrency here