0

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.

Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • 1
    `ch <- 1` blocks until `<- c`, `receive` is never executed. – mkopriva Apr 19 '18 at 04:55
  • @mkopriva I have edited the question I just wants to know what happens when I send a value to a channel in a go routine. And run 4 go routines in the main go routine to receive the value. Which go routine will receive it. – Himanshu Apr 19 '18 at 04:57
  • 1
    even after edit you have still the same problem. – mkopriva Apr 19 '18 at 05:00
  • What should I do to send the value to multiple go routines. Please help me with this one. What is the problem in my code. I just sending a integer value to a channel and receiving it in another function. I know there is an error but why this is happening – Himanshu Apr 19 '18 at 05:01
  • 1
    One option would be to create a channel for each goroutine you want to send the value to. I don't know if that's the best option though. – mkopriva Apr 19 '18 at 05:03
  • 1
    Read my first comment, that's the reason you're getting the error. `receive` never gets called. To fix this you can send to the channel in its own goroutine. E.g. `go func() { ch<-1 }()`. – mkopriva Apr 19 '18 at 05:05
  • Also if you don't close the channel after sending the one value, you'll leak the other 3 goroutines. – mkopriva Apr 19 '18 at 05:08
  • It is still not working same error even if I use `go func() { ch<-1 }()` inside my receive. Can you please provide me with a code snippet. It would be really helpful – Himanshu Apr 19 '18 at 05:09
  • 1
    No error: https://play.golang.org/p/kwKtPhGR7S8 also no output because `main` terminates before sleep is done. – mkopriva Apr 19 '18 at 05:11
  • It is just sending a value using go routine. Why cannot we send a value to channel directly – Himanshu Apr 19 '18 at 05:18
  • 1
    Don't confuse a buffered with an unbuffered channel. Take the [Go Tour Concurrency part](https://tour.golang.org/concurrency/1). – mkopriva Apr 19 '18 at 06:37
  • Thanks @mkopriva you helped me alot. Only one thing I am not getting is my second question which go routine will run first and what happens to others. – Himanshu Apr 19 '18 at 06:41
  • 2
    The order is unspecified and depends on the implementation of the scheduler or whatever it is that handles goroutines in Go. This means that the goroutine from the 1st loop iteration may not necessarily be the first to be executed. – mkopriva Apr 19 '18 at 06:52
  • Thanks @mkopriva Please add an answer and earn yourself some points. You have made my day. – Himanshu Apr 19 '18 at 06:53
  • Thanks everybody for your answers. – Himanshu Apr 19 '18 at 06:54

2 Answers2

3

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.

rofls
  • 4,993
  • 3
  • 27
  • 37
  • Thanks but if you look at this https://play.golang.org/p/EoKZu_iTTzK. There is not a compulsion to use channel with go routines only. go routine is also a function just running on a different thread. The problem is something else – Himanshu Apr 19 '18 at 06:16
  • Weird, ya I actually thought it would work outside of them. So why doesn't this work then? It's the exact same as the previous version, just trying to write to the channel outside of a gouroutine. https://play.golang.org/p/VG1oixdFOo8 – rofls Apr 19 '18 at 06:17
  • 1
    Good one. Now you yourself get into a question. That's I wanted to know, why is it so that it cannot send a value outside of go routine using channel. Though it is possible. But someone downvoted my question don't know why. – Himanshu Apr 19 '18 at 06:20
  • Oh I see, you're using a fixed length, i.e. buffered channel there. It fails if you change it. So, maybe just unbuffered channels need to be in goroutines. – rofls Apr 19 '18 at 06:22
  • Ya if you make it a buffered channel in the playground it works outside the gouroutine: https://play.golang.org/p/v-xUPM12GB0 – rofls Apr 19 '18 at 06:25
  • Yes you are right. But why is it so that I have to create a buffered channel. What is going behind the scenes. – Himanshu Apr 19 '18 at 06:30
  • 2
    This answer explains it: https://stackoverflow.com/a/18660709/1318734 "If the channel is unbuffered, the sender blocks until the receiver has received the value." So, that causes some issues when the main thread is waiting for something to happen that can't ever start, because it's happening next in the same thread (in this case `receive`). – rofls Apr 19 '18 at 06:37
  • yes I have seen it I am just going to read it. Only one question remains that which go rutine will run first and what happens to other fo routines if the first one already printed the value on a channel. Since channel is empty now. – Himanshu Apr 19 '18 at 06:39
  • the other 3 will leak if you don't close the channel, take the Go Tour @Himanshu. – mkopriva Apr 19 '18 at 06:40
  • @mkopriva Is it going to be an error. Since the channel is empty now. – Himanshu Apr 19 '18 at 06:43
  • 2
    @Himanshu a goroutine together with its scope presumably takes up some amount of memory, however small that may be. So leaking goroutines is, I believe, equivalent to leaking memory. Not something you would want. Just close the channel. – mkopriva Apr 19 '18 at 06:45
  • 1
    If you try to read from the channel that has no values, it will block until something else writes to it. If there are no other goroutines running, that will also result in a deadlock. For example: https://play.golang.org/p/NjCYU1kpbtP However here (https://play.golang.org/p/DRhHJ7LP2ci) we have other goroutines that are still alive, so no deadlock. Notice they don't write to the channel until after the others are done sleeping, as they sleep longer. The ones that read from it just block *their own execution* until the values are written. – rofls Apr 19 '18 at 06:46
  • @mkopriva it's actually only necessary to close the channel in certain situations, as it's garbage collected: https://stackoverflow.com/a/8593986/1318734 "Note that it is only necessary to close a channel if the receiver is looking for a close. Closing the channel is a control signal on the channel indicating that no more data follows." – rofls Apr 19 '18 at 06:52
  • @rofls "When the channel is no longer used, it will be garbage collected." If you have a goroutine that is attempting to read from such a channel, from one that noone is sending into, do you believe that counts as no longer in use? You sure about that? – mkopriva Apr 19 '18 at 06:54
  • If that goroutine is by itself, waiting to read from the channel, it will result in a deadlock, as displayed above. If it's running as a goroutine in a larger program that is still running, I agree it will probably continue to block and wait for a deserted channel, and waste memory and more. So, that is a use case that fits the previous description perfectly: "Closing the channel is a control signal on the channel indicating that no more data follows." As an alternative approach: the consumer goroutine knows how much to expect and terminates itself when the channel has sent what's expected. – rofls Apr 19 '18 at 07:03
  • The problem with closing the channel is there may be more than one goroutine writing to it – rofls Apr 19 '18 at 07:05
2

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.

tolok
  • 31
  • 1