這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
channel是go語言在語言層級提供的goroutine間的通訊機制。我們可以使用channel在兩個或者多個goroutine之間傳遞資訊。channel是進程內的通訊。
channel分為帶緩衝的以及不帶緩衝的。
ch:=make(chan int )建立一個不帶緩衝的channel。ch:=make(chan int,2,5)建立一個帶緩衝的channel,其中len(ch)是2,cap(ch)是5.
1.不帶緩衝的channel使用:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
輸出:
1 2016-05-12 12:50:02.4098584 +0800 CST
3 2016-05-12 12:50:02.4118585 +0800 CST
ff end
4 2016-05-12 12:50:04.4149731 +0800 CST
2 2016-05-12 12:50:04.4149731 +0800 CST
main end
5 2016-05-12 12:50:04.4149731 +0800 CST
根據時間可以分析一下程式的工作流程。首先main函數運行到ch<-2時候就阻塞了。不帶緩衝的channel接收資料後會阻塞,直到從這個channel中讀取資料後,阻塞的程式才恢複。等到ff函數運行到<-ch後,main函數才繼續運行。這是ff函數也在運行。從列印的時間可以看出兩個函數是並發的。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
輸出: 1 2016-05-12 13:03:00.3583545 +0800 CST
3 2016-05-12 13:03:00.3603546 +0800 CST
ff end
4 2016-05-12 13:03:02.3634692 +0800 CST
2 2016-05-12 13:03:02.3634692 +0800 CST
main end
5 2016-05-12 13:03:02.3634692 +0800 CST
根據列印的時間資訊可以分析,main函數在執行到<-ch時,阻塞。不帶緩衝的channel從中讀取資料時會阻塞,直到向其中寫入資料,阻塞的程式才會繼續執行。
2.帶緩衝的channel使用:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
輸出:1 2016-05-12 13:07:27.0666093 +0800 CST
2 2016-05-12 13:07:27.0716096 +0800 CST
main end
5 2016-05-12 13:07:27.0716096 +0800 CST
根據列印資訊可以分析,main函數在執行到ch<-2的時候,沒有阻塞,而是繼續執行。那麼為什麼ff函數沒有來得及運行fmt.Println("3", time.Now())這條程式呢?
從上面不帶channel的程式可以看出,fmt.Println("3", time.Now())這句話啟動並執行時間比fmt.Println("1", time.Now())這句話啟動並執行時間要晚。晚多少時間和cpu有關。我們可以先不管。可以做出假設,ff函數裡面的fmt.Println("3", time.Now())還沒來得及執行,main函數就執行完了,程式就退出了。在這個過程中開的goroutine都不會執行,直接取消。
那麼,怎麼證明上面那段程式ff沒有執行是因為main結束了呢?可以在mian函數最後等待一小會。
修改上面的程式如下:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
ch <- 2
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
time.Sleep(1 * time.Second)
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
<-ch
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
程式輸出:1 2016-05-12 13:17:44.0658997 +0800 CST
2 2016-05-12 13:17:44.0708999 +0800 CST
main end
5 2016-05-12 13:17:44.0708999 +0800 CST
3 2016-05-12 13:17:44.0678998 +0800 CST
可以看到,main最後等待了一秒時間,然後ff函數來得及執行fmt.Println("3", time.Now())。但是在ff函數執行time.Sleep(2 * time.Second)等待兩秒的時候,mian已經結束了。
更改一下channel讀取和寫入的順序,如下:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go ff(ch)
fmt.Println("1", time.Now())
<-ch
fmt.Println("2", time.Now())
fmt.Println("main end")
fmt.Println("5", time.Now())
}
func ff(ch chan int) {
fmt.Println("3", time.Now())
time.Sleep(2 * time.Second)
ch <- 2
fmt.Println("ff end")
fmt.Println("4", time.Now())
}
輸出:1 2016-05-12 13:20:06.0520208 +0800 CST
3 2016-05-12 13:20:06.055021 +0800 CST
ff end
4 2016-05-12 13:20:08.0571355 +0800 CST
2 2016-05-12 13:20:08.0571355 +0800 CST
main end
5 2016-05-12 13:20:08.0571355 +0800 CST
根據列印資訊可以分析,main函數在執行到<-ch這句話是阻塞,帶緩衝的channel如果裡面沒有資料,從中讀取的話會阻塞程式的運行。等到ff函數執行到ch <- 2時,main函數才從阻塞中恢複。