無緩衝通道與緩衝通道_go
阿新 • • 發佈:2020-12-15
go無緩衝通道與緩衝通道區別(實驗)
無緩衝通道
傳送和接收操作在另一端準備好之前都會阻塞。
這使得 Go 程可以在沒有顯式的鎖或競態變數的情況下進行同步。
死鎖的情況:
//fatal error: all goroutines are asleep - deadlock!
package main
func main() {
c := make(chan int)
func(c chan int) {
c <- 1
}(c)
<-c
}
改正:在通道寫入的同時,需要讀段讀資料,否則程式阻塞陷入死鎖狀態
package main
func main() {
c := make(chan int)
go func(c chan int) {
c <- 1
}(c)
<-c
}
緩衝通道
僅當通道的緩衝區填滿後,向其傳送資料時才會阻塞。當緩衝區為空時,接受方會阻塞。
正常情況:只要一次性寫入不超過緩衝區大小,則不會造成阻塞
package main
func main() {
c := make(chan int, 1)
func(c chan int) {
c <- 1
}(c)
<-c
}
死鎖情況:
package main
func main() {
c := make(chan int, 1)
func(c chan int) {
c <- 1
c <- 1
}(c)
<-c
}
解決死鎖問題
- 一邊讀取,一邊寫入,保證一次性寫入不會超過緩衝區大小
- 申請更大的緩衝區
對比無緩衝通道與緩衝通道
無緩衝通道:寫入後需等待讀取完成,才解除阻塞狀態
package main
import (
"fmt"
"time"
)
func main() {
out := make(chan int)
go func(in chan int) {
time.Sleep(time.Second)
fmt.Println("Read:", <-in)
}(out)
out <- 2
fmt.Println("Sent.")
time.Sleep(time.Second * 2) // 保證go程執行完成
}
Output:
Read: 2
Sent.
緩衝通道:只要一次性寫入量小於緩衝區大小,則不會阻塞
package main
import (
"fmt"
"time"
)
func main() {
out := make(chan int, 1) // 區別在這裡:申請了大小為1的緩衝區
go func(in chan int) {
time.Sleep(time.Second)
fmt.Println("Read:", <-in)
}(out)
out <- 2
fmt.Println("Sent.")
time.Sleep(time.Second * 2) // 保證go程執行完成
}
Output:
Sent.
Read: 2
進一步探討:可以用無緩衝通道的阻塞性質來回收子程序
for _, u := range urls {
go func(url string) {
Crawl(url, depth-1, fetcher, um)
done <- true
}(u)
}
for i, u := range urls {
fmt.Printf("[%d]waiting for child %d:%s to end.\n", depth, i, u)
<-done
}