2017-05-03 48 views
1

當我做一些去實踐代碼,我遇到一個通道可以這樣兩次關閉了一個問題:爲什麼Go的頻道可以關閉兩次?

// jobs.go 

package main 

import (
    "fmt" 
) 

func main() { 
    fmt.Println("Hello, playground") 
    jobs := make(chan int, 5) 
    done := make(chan bool) 

    go func() { 
     for { 
      j,more := <-jobs 

      fmt.Println("receive close: ", j, more) 
      done <- true 
     } 
    }() 

    close(jobs) 
    <- done 
} 

輸出:

~ go run jobs.go 
Hello, playground 
receive close: 0 false 
receive close: 0 false 

但是,當我手動兩次關閉通道,我得到了panic: close of closed channel

爲什麼上面的代碼可以接收兩次?

回答

3

一個頻道只能關閉一次,試圖關閉一個封閉的頻道恐慌。

但是,從封閉的通道receiving沒有限制,從關閉信道接收:

甲接收在closed信道操作可總是立即進行,得到的元素類型的zero value任何先前發送的值之後已經接收。

甲轉到應用運行,直到其主要的goroutine運行(給定的「正常」的情況下),或者從另一種觀點:當其主要夠程終止轉到應用程序終止,也就是,main()函數返回。它並不等待其他非門戶配置完成。

你開始了一個無盡的for循環的第二個goroutine,沒有終止的方式。因此,該循環將繼續進行,直到main()函數(它運行在併發主要goroutine返回中)。由於for循環首先從jobs收到,它將等待主要goroutine關閉它(此接收操作只能繼續)。然後主要的例程想要從done收到,等到第二個goroutine發送一個值。然後主辦公室隨時「自由」終止。運行循環的第二個goroutine可能會從jobs收到一個附加值,但隨後發送的done將會阻止,因爲沒有人從它接收(而且沒有緩衝)。

從一個信道接收,直到它關閉正常使用for range做,如果通道被關閉,其退出:

for j := range jobs { 
    fmt.Println("received: ", j) 
    done <- true 
} 

當然,這會導致你的情況陷入僵局,作爲循環體將永遠達到因爲沒有人發送jobs上的任何東西,因此循環將永遠不會進入其主體發送done的值,這是主要常規等待的內容。

0

jobs通道未關閉兩次。當調用close(jobs)時,它僅關閉一次。你看到的輸出是因爲goroutine和主線程正在執行。

當goroutine發生火災時,它不會立即開始運行。程序控制轉到此部分:

close(jobs) // <-- 
    <- done 
} 

jobs已關閉。主線程然後掛在done上的下一個接收。

現在發生上下文切換並且goroutine開始運行。它從關閉的jobs中讀取,打印適當的值(false表示more表示封閉通道),並沿着done發送true

但是,循環能夠再次執行並且goroutine在done上的下一次發送中阻塞。現在main再次醒來,收到done並終止程序。

1

去頻道沒有關閉twise。你是路過完成< - 真正的第一個打印後

j,more := <-jobs 

fmt.Println("receive close: ", j, more) 
done <- true 

所以它印兩個時間

receive close: 0 false 
receive close: 0 false 

如果您使用做< - 打印之前真,那麼它將打印僅一次,將得到封閉。

done <- true 
j,more := <-jobs 

fmt.Println("receive close: ", j, more) 

輸出:

receive close: 0 false