1. 程式人生 > 其它 >無緩衝通道與緩衝通道_go

無緩衝通道與緩衝通道_go

技術標籤:gogolang多執行緒

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
}

解決死鎖問題

  1. 一邊讀取,一邊寫入,保證一次性寫入不會超過緩衝區大小
  2. 申請更大的緩衝區



對比無緩衝通道與緩衝通道

無緩衝通道:寫入後需等待讀取完成,才解除阻塞狀態

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
	}