Search code examples
gogoroutine

Concurrent execution but serialized output


I need to use 3 goroutines named g1, g2, g3. and distribute numbers from 1-10 among the above 3 goroutines in a round-robin fashion. They will do some hypothetical work based on the provided number. And program should print output in the following manner.

g1-1
g2-2
g3-3
g1-4
g2-5
g3-6
...

Tasks must be performed concurrently but the output must be in sequential order.

I have implemented the below code which distributes numbers and prints but output print order is not guaranteed as mentioned above.

I need some help to fix the below code or suggestions on another approach to get the above-desired output.

Approach 1:

package main

import (
    "fmt"
    "sync"
)

func main() {

    chang1 := make(chan int)
    chang2 := make(chan int)
    chang3 := make(chan int)

    var wg sync.WaitGroup
    wg.Add(3)

    go func() {
        for num := range chang1 {
            fmt.Println("g1", num)
        }
        wg.Done()
    }()

    go func() {
        for num := range chang2 {
            fmt.Println("g2", num)
        }
        wg.Done()

    }()

    go func() {
        for num := range chang3 {
            fmt.Println("g3", num)
        }
        wg.Done()

    }()

    channels := []chan int{chang1, chang2, chang3}

    for i := 1; i <= 10; i++ {

        currentCh := (i - 1) % 3
        channels[currentCh] <- i

    }

    close(chang1)
    close(chang2)
    close(chang3)
    wg.Wait()

}

output (with incorrect sequence)

g1- 1
g1- 4
g2- 2
g3- 3
g1- 7
g2- 5
g2- 8
g3- 6
g3- 9
g1- 10
...


Solution

  • if you don't want to use slice then I think something like this will work:- (playground)

    package main
    
    import (
        "fmt"
    )
    
    func main() {
    
        chang1 := make(chan string)
        chang2 := make(chan string)
        chang3 := make(chan string)
    
        channels := []chan string{chang1, chang2, chang3}
    
        for i := 1; i < 10; i += 3 {
    
            go g1(i, channels[i%3])
            go g2(i+1, channels[(i+1)%3])
            go g3(i+2, channels[(i+2)%3])
    
            fmt.Print(<-channels[i%3])
            fmt.Print(<-channels[(i+1)%3])
            fmt.Print(<-channels[(i+2)%3])
        }
    
    }
    
    func g1(i int, chanel chan string) {
    
        chanel <- fmt.Sprintln("g1", i)
    
    }
    
    func g2(i int, chanel chan string) {
    
        chanel <- fmt.Sprintln("g2", i)
    
    }
    
    func g3(i int, chanel chan string) {
    
        chanel <- fmt.Sprintln("g3", i)
    
    }
    
    output 
    g1 1
    g2 2
    g3 3
    g1 4
    g2 5
    g3 6
    g1 7
    g2 8
    g3 9
    

    but keep in mind in this solution you have to run 3 goroutines then wait for all of them to give the result then go back

    if this is not ok for you you need to use bufferd channels.