-
Channel
- 是阻塞同步的
- 通過make創建,close關閉
- Channel是引用類型
- 可以使用for range 來迭代不斷操作channel
- 可以設置單向或雙向通道
- 可以設置緩存大小,在未被填滿前不會發生阻塞
-
Select
- 可以處理一個或多個channel的發送與接收
- 同時有多個可用的channel時按隨機順序處理
- 可用空的select來阻塞main函數
- 可設置超時
一個簡答的例子
...
func main(){
go work()
}
func work(){
fmt.Println("Go!")
}
輸入:
(什麼都沒有)
什麼都沒有輸出,原因是由代碼可以看出,啓動了一個攜程,在work函數還沒有打印主函數已經結束了。
爲了讓work裏的打印可以輸出,讓主線程睡1秒中,這是爲了讓攜程可以執行完畢,代碼修改如下:
...
func main(){
go work()
time.sleep(time.Second)
}
func work(){
fmt.Println("Go!")
}
以上僅僅是爲了效果,在實際的開發中不會這樣做,因爲我們無法判斷work函數的執行邏輯到底需要多長時間。
藉助Channel,替換time.sleep
package main
import (
"fmt"
)
func main() {
c:= make(chan bool)
go work(c)
<- c //這裏會阻塞,直到c裏有值
}
func work(c chan bool){
fmt.Println("Go Go Go!")
c <- true
}
輸出:
Go Go Go !
注意:以上是個簡單的程序,只向channel放了一個值,當取出後,資源就會自動釋放了,程序就結束了,所以不用再調用close關閉通道了。
上面的程序從通道里取值,沒有賦值給任何變量也沒有輸出,如下使用range從通道里將值打印出來:
package main
import (
"fmt"
)
func main() {
c:= make(chan bool)
go work(c)
<- c
for v:= range c{
fmt.Println(v)
}
}
func work(c chan bool){
fmt.Println("Go Go Go!")
c <- true
close(c)
}
輸出:
Go Go Go!
true
以上程序修改了:11-13行使用range 將值打印出來, 19行調用了close函數關閉通道,如果不關閉通道,就會不斷的遍歷通道造成死鎖。
for i := range c
能夠不斷的讀取channel裏面的數據,直到該channel被顯式的關閉。關閉channel之後就無法再發送任何數據了,在消費方可以通過語法v,ok := <-ch
測試channel是否被關閉。如果ok返回false,那麼說明channel已經沒有任何數據並且已經被關閉。
設置有緩存的通道
c:= make(chan bool,1)
有緩存的就是channel可以存儲多少元素。ch:= make(chan bool, 4)
,創建了可以存儲4個元素的bool 型channel。在這個channel 中,前4個元素可以無阻塞的寫入。當寫入第5個元素時,代碼將會阻塞,直到其他goroutine從channel 中讀取一些元素,騰出空間。
有緩存和無緩存的區別:有緩存的是異步的,無緩存是同步阻塞的。如下示例:
package main
import (
"fmt"
)
func main() {
c:= make(chan bool,1)
go func(){
fmt.Println("Go Go Go!")
<- c
}()
c <- true
}
以上程序不會有任何輸出,以上創建了有緩存的通道,在執行14行的時候,程序不會阻塞,就不會等待攜程的執行。
package main
import (
"fmt"
)
func main() {
c:= make(chan bool,1)
go func(){
fmt.Println("Go Go Go!")
c <- true
}()
<- c
}
輸出:
Go Go Go!
緩存通道的例子
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
c := make(chan bool,10)
for i := 0; i < 10; i++ {
go Go(c, i)
}
for i := 0; i < 10; i++ {
<-c
}
}
func Go(c chan bool, index int) {
a := 1
for i := 0; i <= 10000000; i++ {
a += i
}
fmt.Println(index, a)
c <- true
}
實現以上程序的另一個辦法:
package main
import (
"fmt"
"sync"
)
func main() {
wg:= sync.WaitGroup{}
wg.Add(10)
for i := 0; i < 10; i++ {
go Go(&wg, i)
}
wg.Wait()
}
func Go(wg *sync.WaitGroup, index int) {
a := 1
for i := 0; i <= 10000000; i++ {
a += i
}
fmt.Println(index, a)
wg.Done()
}
輸出:
0 50000005000001
5 50000005000001
9 50000005000001
1 50000005000001
7 50000005000001
6 50000005000001
3 50000005000001
2 50000005000001
4 50000005000001
8 50000005000001