這種模式的主要目的是確保資料只在一個 goroutine 中被訪問,從而避免了競爭條件和其他並發相關的問題。
封閉模式大致可以分為兩種:不可公開封閉和可公開封閉。
不可公開封閉(Ad hoc confinement):
這是一種隱式的封閉方式,因為它完全依賴於開發人員的紀律。
在這種模式下,開發人員簡單地確保某一資料結構只在一個 goroutine 中被訪問,並且不會被共享或傳遞到其他 goroutines。
這種方式的缺點是,由於它完全依賴於開發者的紀律,所以可能容易出錯。
func main() {
data := make([]int, 4)
loopData := func(handleData chan<- int) {
defer close(handleData)
for i := range data {
handleData <- data[i]
}
}
handleData := make(chan int)
go loopData(handleData)
for num := range handleData {
fmt.Println(num)
}
}
這個範例程式碼僅僅是通過確保 data 切片的使用方式(即哪些操作是在哪個 goroutine 中執行的)來達到Ad hoc confinement,而不是通過語言特性(例如,通過某種封閉模式)來確保這一點。
可公開封閉(Lexical confinement):
這是一種明確的封閉方式,因為它依賴於語言的語法和結構。
一個典型的例子是使用 channels 來傳遞資料。當你從一個 goroutine 將資料傳遞到另一個 goroutine 時,你可以使用 channels,這樣可以確保資料的所有權只存在於一個 goroutine。
例如,如果你在一個 goroutine 中寫入資料到一個 channel,並在另一個 goroutine 中從該 channel 讀取資料,那麼你可以確保該資料在任何時候都只由一個 goroutine 訪問。
func main() {
chanOwner := func() <-chan int {
results := make(chan int, 5) // <1>
go func() {
defer close(results)
for i := 0; i <= 5; i++ {
results <- i
}
}()
return results
}
consumer := func(results <-chan int) { // <3>
for result := range results {
fmt.Printf("Received: %d\n", result)
}
fmt.Println("Done receiving!")
}
results := chanOwner() // <2>
consumer(results)
}
這段程式碼使用 lexical confinement 的方法,確保只有 channel 的建立者可以寫入到該 channel,而其他部分,如消費者,只能讀取。這是一個非常常見的模式,因為它提供了一個明確的方式來確保資料的一致性和安全性,避免在多個 goroutines 之間產生競爭條件。