18.8 協程(goroutine)與通道(channel)
出於性能考慮的建議:
實踐經驗表明,如果你使用並行運算獲得高於串行運算的效率:在協程內部已經完成的大部分工作,其開銷比創建協程和協程間通信還高。
1 出於性能考慮建議使用帶緩存的通道:
使用帶緩存的通道可以很輕易成倍提高它的吞吐量,某些場景其性能可以提高至10倍甚至更多。通過調整通道的容量,甚至可以嘗試着更進一步的優化其性能。
2 限制一個通道的數據數量並將它們封裝成一個數組:
如果使用通道傳遞大量單獨的數據,那麼通道將變成性能瓶頸。然而,將數據塊打包封裝成數組,在接收端解壓數據時,性能可以提高至10倍。
創建:ch := make(chan type,buf)
(1)如何使用for
或者for-range
遍歷一個通道:
for v := range ch {
// do something with v
}
(2)如何檢測一個通道ch
是否關閉:
//read channel until it closes or error-condition
for {
if input, open := <-ch; !open {
break
}
fmt.Printf("%s", input)
}
或者使用(1)自動檢測。
(3)如何通過一個通道讓主程序等待直到協程完成:
(信號量模式):
ch := make(chan int) // Allocate a channel.
// Start something in a goroutine; when it completes, signal on the channel.
go func() {
// doSomething
ch <- 1 // Send a signal; value does not matter.
}()
doSomethingElseForAWhile()
<-ch // Wait for goroutine to finish; discard sent value.
如果希望程序一直阻塞,在匿名函數中省略 ch <- 1
即可。
(4)通道的工廠模板:以下函數是一個通道工廠,啓動一個匿名函數作爲協程以生產通道:
func pump() chan int {
ch := make(chan int)
go func() {
for i := 0; ; i++ {
ch <- i
}
}()
return ch
}
(5)通道迭代器模板:
(6)如何限制併發處理請求的數量:參考章節14.11
(7)如何在多核CPU上實現並行計算:參考章節14.13
(8)如何終止一個協程:runtime.Goexit()
(9)簡單的超時模板:
timeout := make(chan bool, 1)
go func() {
time.Sleep(1e9) // one second
timeout <- true
}()
select {
case <-ch:
// a read from ch has occurred
case <-timeout:
// the read from ch has timed out
}
(10)如何使用輸入通道和輸出通道代替鎖:
func Worker(in, out chan *Task) {
for {
t := <-in
process(t)
out <- t
}
}
(11)如何在同步調用運行時間過長時將之丟棄:參考章節14.5 第二個變體
(12)如何在通道中使用計時器和定時器:參考章節14.5
(13)典型的服務器後端模型:參考章節14.4