How to avoid deadlock in this golang program?

Here is my program that creates a dead end, how can I avoid it and what is the recommended template for handling this kind of situation.

The problem after the timeout, how to determine that there is no reader on my channel?

var wg sync.WaitGroup

func main() {   
    wg.Add(1)
    c := make(chan int)
    go readFromChannel(c, time.After(time.Duration(2)*time.Second))
    time.Sleep(time.Duration(5) * time.Second)
    c <- 10
    wg.Wait()
}

func readFromChannel(c chan int, ti <-chan time.Time) {
    select {
    case x := <-c:
        fmt.Println("Read", x)
    case <-ti:
        fmt.Println("TIMED OUT")
    }
    wg.Done()
}
+4
source share
4 answers

So let's see what really happens in your source. You have two goroutines (there are more than two, but we are going to focus on explicit ones), mainand readFromChannel.

Let's see what it does readFromChannel:

if channel `c` is not empty before `ti` has expired, print its contents and return, after signalling its completion to wait group.
if `ti` has expired before `c` is not empty, print "TIMED OUT" and return, after signalling its completion to wait group.

now Main:

adds to waitgroup 
make a channel `c`
start a goroutine `readFromChannel`
sleep for 5 seconds
send 10 to channel `c`
call wait for waitgroup

Now you can go through the execution thread for your code at the same time (your code may / may not be executed in this order every time, remember this)

1) wg.Add(1)
2) c := make(chan int)
3) go readFromChannel(c, time.After(time.Duration(2)*time.Second))
#timer ti starts#
4) time.Sleep(time.Duration(5) * time.Second)
#MAIN Goroutine begins sleep
#timer ti expires#
5) case <-ti:
6) fmt.Println("TIMED OUT")
7) wg.Done()
# readFromChannel Goroutine returns #
#MAIN Goroutine exits sleep#
8) c<-10
9) ......#DEADLOCK#

, . , - , , . , c <- 10 , - c, goroutine, , 2 . , c , main - goroutine, Deadlock.

? , receive send. , "" .

:

func main() {
    wg.Add(1)
    c := make(chan int)
    go readFromChannel(c, time.After(time.Duration(2)*time.Second))
    time.Sleep(time.Duration(5) * time.Second)
    c <- 10
    wg.Wait()
}

func readFromChannel(c chan int, ti <-chan time.Time) {
        // the forloop will run forever
    loop: // **
    for {
        select {
            case x := <-c:
                    fmt.Println("Read", x)
                    break loop // breaks out of the for loop and the select **
            case <-ti:
                    fmt.Println("TIMED OUT")
            }
    }
    wg.Done()
} 

** .

+7

. docs:

, , . , ,

, .

c := make(chan int, 10) // holds 10 ints

https://golang.org/doc/effective_go.html#channels, , .

+1

, select, goroutine.

go func() {
    for {
        select {
        case x := <-c:
            fmt.Println("Read", x)
        case <-ti:
            fmt.Println("TIMED OUT")
        }
    }
}()

goroutines select, .

Using the send operation in the select statement with the default case ensures that the send is non-blocking! If there are no cases, the selection blocks execution forever.

https://play.golang.org/p/Ai1ggveb4s

0
source

This is an old question, but I dive deep into the learning channels and found it here.

I think you just need to close the channel after you send it?

the code:

func main() {
    wg.Add(1)
    c := make(chan int)
    go readFromChannel(c, time.After(time.Duration(2)*time.Second))
    time.Sleep(time.Duration(5) * time.Second)
    c <- 10
    close(c) // <- CLOSE IT HERE
    wg.Wait()
}
0
source

All Articles