Gabriel
Gabriel

Reputation: 47

Golang channel missing some values

I have the following code

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    concurrent := 12
    ch := make(chan int)
    defer close(ch)
    for i := 1; i <= concurrent; i++ {
        go worker(i, ch)
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
}

func worker(worker int, ch chan int) {
    for requestId := range ch {
        fmt.Printf("worker %d is requesting to %d...\n", worker, requestId)
        time.Sleep(time.Duration(rand.Intn(3)) * time.Second)
    }
}

The main function starts multiple goroutines running the worker function and then put some values in the channel, which are printed by the worker. My problem is that some times the last value or even more values in the channel are not printed by the worker.

worker 10 is requesting to 10...
worker 5 is requesting to 1...
worker 5 is requesting to 13...
worker 7 is requesting to 6...
worker 2 is requesting to 2...
worker 3 is requesting to 7...
worker 3 is requesting to 14...
worker 6 is requesting to 4...
worker 11 is requesting to 9...
worker 9 is requesting to 8...
worker 9 is requesting to 15...
worker 12 is requesting to 11...
worker 8 is requesting to 12...
worker 8 is requesting to 16...
worker 4 is requesting to 5...
worker 1 is requesting to 3...
worker 11 is requesting to 17...

I think it's because the main function ends and "kill" all runnning goroutines before the last value be printed because with a sleep in the end all values are always printed.

func main() {
    concurrent := 12
    ch := make(chan int)
    defer close(ch)
    for i := 1; i <= concurrent; i++ {
        go worker(i, ch)
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
    time.Sleep(3 * time.Second)
}

How can i solve this problem without using sleep and only using channels if possible?

Upvotes: 0

Views: 628

Answers (1)

shizhz
shizhz

Reputation: 12501

Because the main goroutine exited right after the last for loop which caused the whole program exited as well, the background goroutines may or may not got its chance to run, you have to provide some synchronization way to "wait" all of your worker goroutines to finish.

Use sync.WaitGroup

   func main() {

    concurrent := 12
    ch := make(chan int)

    var wg sync.WaitGroup
    for i := 1; i <= concurrent; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            worker(i, ch)
        }(i) // You have to pass i as parameter
    }
    for i := 1; i <= 21; i++ {
        ch <- i
    }
    close(ch) // Close channel to tell all workers to stop

    wg.Wait() // Wait all workers to finish its work
   }

Upvotes: 2

Related Questions