Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoid panic, when trying to insert a value to a closed channel

Tags:

go

package main

import (
    "fmt"
    "time"
)

func fib() chan int {
    c := make(chan int)

    go func() {
        c <- 0
        c <- 1

        n, m := 0, 1
        for {
            temp := n + m
            n = m
            m = temp
            c <- m // This results in panic, when the channel is closed
        }
    }()

    return c
}

func main() {
    start := time.Now()
    var lastFib int
    c := fib()

    for i := 0; i != 1000000; i++ {
        lastFib = <-c
    }

    close(c)
    fmt.Println(lastFib)
    fmt.Println(time.Now().Sub(start))
}

In the most idiomatic way, how would one avoid the panic in the goroutine, when the channel is closed? Or should i avoid using close at all?

I'm not looking into alternative methods (such as closures) to achieve the same thing, just trying to get a better understanding of channels.

like image 625
Dante Avatar asked Jan 09 '14 06:01

Dante


People also ask

What happens when a channel is closed?

Once a channel is closed, we can't send data to it, though we can still read data from it. A closed channel denotes a case where we want to show that the work has been done on this channel, and there's no need for it to be open. We open the channel the moment we declare one using the make keyword.

What happens if you close a closed channel Golang?

closing a closed channel will panic, so it is dangerous to close a channel if the closers don't know whether or not the channel is closed. sending values to a closed channel will panic, so it is dangerous to send values to a channel if the senders don't know whether or not the channel is closed.

What happens when you read from a closed channel?

The value read from a closed channel will be the zero value of the channel's type. For example, if the channel is an int channel, then the value received from a closed channel will be 0 . The for range form of the for loop can be used to receive values from a channel until it is closed.

How do you stop Goroutine?

Typically, you pass the goroutine a (possibly separate) signal channel. That signal channel is used to push a value into when you want the goroutine to stop. The goroutine polls that channel regularly. As soon as it detects a signal, it quits.


2 Answers

Close is a good way for the goroutine sending into a channel to signal the receiving side that you are done with this channel. The other way around (your problem) is IMHO undoable, at least direct. You could add an other channel done which signal end of duty to your fibonacci generating goroutine.

like image 147
Volker Avatar answered Sep 29 '22 19:09

Volker


Here is a modified version of your example that uses channels in an allowed (though not necessarily sensible) way:

package main

import (
    "fmt"
    "time"
)

func fib(c chan int) {
    c <- 0
    c <- 1

    n, m := 0, 1
    for {
        temp := n + m
        n = m
        m = temp
        c <- m
        if m > 100000000 {
            close(c)
            break
        }
    }
}

func main() {
    start := time.Now()
    lastFib, newFib := 0, 0
    ok := true
    c := make(chan int)
    go fib(c)

    for {
        newFib, ok = <-c
        if !ok {
            fmt.Println(lastFib)
            break
        }
        lastFib = newFib
    }

    fmt.Println(time.Now().Sub(start))
}
like image 39
Dmitri Goldring Avatar answered Sep 29 '22 17:09

Dmitri Goldring