dongqufi82315 2017-01-21 17:08
浏览 29
已采纳

写入到频道永远被阻止

I am stuck in a strange situation where the write operation to the channel never happens.

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan int)
    s := make(chan bool)
    k := make(chan bool)
    fmt.Println("start")
    go func() {
    fmt.Println("routine 1")
        s <- true
    }()
    go func() {
    fmt.Println("routine 2")
        for {
            select {
            case  <-s :
                for i := 0; i < 3; i++ {
                    fmt.Println("before")
                    c <- i
                    fmt.Println("after")
                }       
            case <- k :
                fmt.Println("k ready")
                break
            }
        }
    }()

    go func() {
        fmt.Println("routine 3")
        for {
            select {
                case x := <- c :
                fmt.Println("x=", x)
                k <- true
                fmt.Println("k done")

            }
        }
    }()

    time.Sleep(1000 * time.Millisecond)
}

And here is the output:

start
routine 1
routine 2
before
routine 3
x= 0
after
before

I wonder why the write to the channel k blocks, but the log statement fmt.Println("k ready") is never printed.

Here is what I think :

  • go routine 1 writes true to channel s
  • go routine 2 writes 0 to channel c and waits because buffer size is 0, it will not be able to write '1' to it unless someone reads channel c
  • go routine 3 comes into the picture, reads channel c (now go routine 2 can write to c once go routine 2 resumes) prints the value of x. NOW IT SHOULD BE ABLE TO WRITE TO CHANNEL K but that is not happening

According to me it should be able to write to channel k then case 2 of goroutine should execute and print "k ready"

Can anyone explain me why write to the channel blocked? As a fix I know I can increase the buffer size of channel c and everything will get printed but I am not interested in fixing this, instead I want to understand this scenario.

A nice blog to understand the above case.

  • 写回答

1条回答 默认 最新

  • dsv17139 2017-01-21 17:21
    关注

    You have a deadlock.

    • goroutine 1 writes to s then quits
    • goroutine 2 reads from s, and writes to c
    • goroutine 3 reads from c, and writes to k, and this blocks because nothing is reading from k, because goroutine 2 is blocked in the write to k above.
    • goroutine 2 writes to c again which blocks as goroutine 3 is still trying to write to k and thus is not reading from c

    Contrary to what you say, you don't have a buffer size of 1. You have a buffer size of zero (i.e. an unbuffered channel), so a write will block until something reads. This is probably the source of your misunderstanding. Per the language specification:

    A new, initialized channel value can be made using the built-in function make, which takes the channel type and an optional capacity as arguments:

    make(chan int, 100)
    

    The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives). A nil channel is never ready for communication.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 机器学习能否像多层线性模型一样处理嵌套数据
  • ¥20 西门子S7-Graph,S7-300,梯形图
  • ¥50 用易语言http 访问不了网页
  • ¥50 safari浏览器fetch提交数据后数据丢失问题
  • ¥15 matlab不知道怎么改,求解答!!
  • ¥15 永磁直线电机的电流环pi调不出来
  • ¥15 用stata实现聚类的代码
  • ¥15 请问paddlehub能支持移动端开发吗?在Android studio上该如何部署?
  • ¥20 docker里部署springboot项目,访问不到扬声器
  • ¥15 netty整合springboot之后自动重连失效