Himanshu
Himanshu

Reputation: 12675

Send value through channel to multiple go routines

I want to send a value in a channel to go routines from main function. What happens is which go routine will receive the value from the channel first.

package main

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

func main() {
    var ch chan int
    ch = make(chan int)
    ch <- 1
    receive(ch)
}

func receive(ch chan int){
    for i := 0; i < 4; i++ {
        // Create some threads
        go func(i int) {
            time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
            fmt.Println(<-ch)
        }(i)
    }
}

My current implementation is giving an error.

fatal error: all goroutines are asleep - deadlock!

How can I know that which go routine will receive the value from the channel first. And what happens to other go routine If those will run or throw an error since there is no channel to receive the value. As it is already received by one of them.

If a create a buffered channel my code works. So I don't get it what has happened behind the scene which is making it work when creating a buffered channel like below:

func main() {
    var ch chan int
    ch = make(chan int, 10)
    ch <- 1
    receive(ch)
}

If we look at below code. I can see that we can send values through channels directly there is no need of creating a go routine to send a value thorugh a channel to another go routines.

package main

import "fmt"

func main() {

    // We'll iterate over 2 values in the `queue` channel.
    queue := make(chan string, 2)
    queue <- "one"
    queue <- "two"
    close(queue)

    for elem := range queue {
        fmt.Println(elem)
    }
}

Then what is wrong with my code. Why is it creating a deadlock.

Upvotes: 0

Views: 3481

Answers (2)

rofls
rofls

Reputation: 5115

An unbuffered channel (without a length) blocks until the value has been received. This means the program that wrote to the channel will stop after writing to the channel until it has been read from. If that happens in the main thread, before your call to receive, it causes a deadlock.

There are two more issues: you need to use a WaitGroup to pause the completion until finished, and a channel behaves like a concurrent queue. In particular, it has push and pop operations, which are both performed using <-. For example:

//Push to channel, channel contains 1 unless other things were there
c <- 1
//Pop from channel, channel is empty
x := <-c

Here is a working example:

package main

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

func main() {
        var ch chan int 
        ch = make(chan int)
        go func() {
                ch <- 1
                ch <- 1
                ch <- 1
                ch <- 1
        }() 
        receive(ch)
}

func receive(ch chan int) {
        wg := &sync.WaitGroup{}
        for i := 0; i < 4; i++ {
                // Create some threads
                wg.Add(1)
                go func(i int) {
                        time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
                        fmt.Println(<-ch)
                        wg.Done()
                }(i)
        }   
        wg.Wait()
        fmt.Println("done waiting")
}

Playground Link

As you can see the WaitGroup is quite simple as well. You declare it at a higher scope. It's essentially a fancy counter, with three primary methods. When you call wg.Add(1) the counter is increased, when you call wg.Done() the counter is decreased, and when you call wg.Wait(), the execution is halted until the counter reaches 0.

Upvotes: 3

tolok
tolok

Reputation: 31

If all you need is to start several workers and send a task to any of them, then you'd better run workers before sending a value to a channel, because as @mkopriva said above, writing to a channel is a blocking operation. You always have to have a consumer, or the execution will freeze.

func main() {
    var ch chan int
    ch = make(chan int)

    receive(ch)

    ch <- 1
}

func receive(ch chan int) {
    for i := 0; i < 4; i++ {
        // Create some threads
        go func(i int) {
            time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
            fmt.Printf("Worker no %d is processing the value %d\n", i, <-ch)
        }(i)
    }
}

Short answer for the question "Which go routine will receive it?" - Whatever. :) Any of them, you can't say for sure.

However I have no idea what is time.Sleep(...) for there, kept it as is.

Upvotes: 2

Related Questions