select 實例
Go 語言選擇(select)可以等待多個通道操作。將 goroutine 和 channel 與 select 結(jié)合是 Go 語言的一個強大功能沽瘦。看起來可能和 switch 相似,但是并不是史飞。
對于這個示例猫妙,將選擇兩個通道瓷翻。
每個通道將在一段時間后開始接收值,以模擬阻塞在并發(fā) goroutines 中執(zhí)行 RPC 操作割坠。我們將使用 select 同時等待這兩個值齐帚,在每個值達到時打印它們。
執(zhí)行實例程序得到的值是 one 彼哼, 然后是 two 对妄。注意,總執(zhí)行時間只有 1-2 秒 Sleeps 同時執(zhí)行敢朱。
package main
import (
"fmt"
"time"
)
func main(){
// channel
c1 := make (chan string)
c2 := make (chan string)
go func(){
time.Sleep(time.Second * 1)
c1 <- "ONE"
}()
go func(){
time.Sleep(time.Second * 2)
c2 <- "TWO"
}()
for i := 0 ; i < 2 ; i ++ {
select {
case msg1 := <-c1:
fmt.Println("received~one",msg1)
case msg2 := <-c2:
fmt.Println("received~two",msg2)
}
}
}
超時(timeouts)實例
超時對于連接到外部資源或在不需要綁定執(zhí)行時間的程序很重要剪菱。在 Go 編程中由于使用了通道和選擇 (select),實現(xiàn)超時是很容易和優(yōu)雅的拴签。
在這個示例中孝常,假設(shè)正在執(zhí)行一個外部調(diào)用,2秒后在通道 C1 上返回其結(jié)果篓吁。
這里是 select 實現(xiàn)超時茫因。res := <-c1 等待結(jié)果和 <-Time 。等待在超時 1 秒后發(fā)送一個值杖剪。由于選擇繼續(xù)準備好第一個接收冻押,如果超時超過允許的 1 秒,則將按照超時情況處理盛嘿。
如果允許更長的超時洛巢,如: 3s ,那么從 c2 的接收將成功次兆,這里將會打印稿茉。
運行此程序顯示第一個操作超時和第二個操作超時。
使用此選擇超時模式需要通過通道傳達結(jié)果芥炭。這是一個好主意漓库,因為其他重要的 Go 功能是基于渠道和 Select。現(xiàn)在看看下面的兩個例子:計時器和ticker 园蝠。
package main
import (
"time"
"fmt"
)
func main(){
c1 := make(chan string,1)
go func(){
time.Sleep(time.Second *2)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(time.Second *1):
fmt.Println("timeout 1")
}
c2 := make(chan string , 1)
go func() {
time.Sleep(time.Second * 2)
c2 <- "result 2"
}()
select {
case res := <-c2:
fmt.Println(res)
case <-time.After(time.Second *3):
fmt.Println("timeout2")
}
}
非阻塞通道操作實例
通道的基本發(fā)送和接受都阻塞渺蒿。但是,可以使用 select 和 default 子句來實現(xiàn)非阻塞發(fā)送彪薛,接收茂装,甚至非阻塞多路選擇( select )怠蹂。
這里會是一個非阻塞接收。如果消息上有可用的值少态,則選擇將使用該值的 <-message 城侧。如果不是,它會立即采取默認情況彼妻。
可以使用多個上面的默認子句來實現(xiàn)多路非阻塞選擇(select)嫌佑。這里嘗試對消息(message) 和信號(signals) 的非阻塞接收。
package main
import (
"fmt"
"time"
)
func main(){
messages := make(chan string)
signals := make(chan bool)
go func() {
messages <- "test"
}()
time.Sleep(time.Second * 1)
select{
case msg := <-messages:
fmt.Println("Received message",msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages<-msg:
fmt.Println("sent message",msg)
default:
fmt.Println("no message sent")
}
select {
case msg := <- messages:
fmt.Println("received message",msg)
case sig := <-signals:
fmt.Println("received signals",sig)
default:
fmt.Println("no activity")
}
}
go 關(guān)閉通道實例
關(guān)閉通道表示不會再發(fā)送值澳骤。這對于將完成通訊到通道的接收器是很有用的歧强。
在這個例子中,我們將使用一個作業(yè)通道來完成從 main goroutine 到 worker goroutine 的工作为肮。當沒有更多的工作時,則將關(guān)閉工作通道肤京。
這里是工作程序 goroutine颊艳。它反復(fù)從j的工作接收 more := <- jobs 。在這種特殊的 2 值形式的接收中忘分。如果作業(yè)已關(guān)閉并且接受到的通道中的所有值棋枕,則 more 的值將為 false。當已經(jīng)完成了所有的工作時妒峦,使用這個通知重斑。
這會通過作業(yè)通道向工作線程發(fā)送 3 個作業(yè),然后關(guān)閉它肯骇。
package main
import "fmt"
func main(){
jobs := make(chan int,5)
done := make(chan bool)
go func() {
for {
j , more := <- jobs
if more{
fmt.Println("received job",j)
}else {
fmt.Println("received all jobs")
done <- true
return
}
}
}()
for j:=1 ; j<= 3 ; j++{
jobs <- j
fmt.Println("send job",j)
}
//關(guān)閉通道
close(jobs)
fmt.Println("send all jobs")
//
fmt.Println(<-done)
}
以上例程中實際輸出的時候遇到輸出為以下樣子:
"D:\Program\Gogland 172.3757.2\bin\runnerw.exe" C:\Go\bin\go.exe run C:/Go/MyGo/src/lesson5_select/close_chan.go
send job 1
send job 2
received job 1
received job 2
received job 3
send job 3
send all jobs
received all jobs
true
個人猜想是因為 goroutine 中執(zhí)行的線程調(diào)度的時候快于 主要 routine窥浪。所以出現(xiàn)以上輸出,但是就是實際執(zhí)行過程的時候一定是接收到發(fā)送過后再輸出笛丙。
go 通道范圍實例
在前面的例子中漾脂,我們已經(jīng)看到了 for 和 range 語句如何為基于數(shù)據(jù)結(jié)構(gòu)提供迭代。還可以使用此語法對從通道接收的值進行迭代胚鸯。
此范圍在從隊列中接收到的每個元素上進行迭代骨稿。因為關(guān)閉了上面的通道,迭代在接收到 2 個元素后終止姜钳。
這個示例還可以關(guān)閉非空信道坦冠,但仍接收剩余值。
package main
import "fmt"
func main(){
//存放兩個值在通道中
queue := make(chan string,2)
queue <- "one"
queue <- "two"
close(queue)
for elem:=range queue{
fmt.Println(elem)
}
}
輸出結(jié)果如下:
"D:\Program\Gogland 172.3757.2\bin\runnerw.exe" C:\Go\bin\go.exe run C:/Go/MyGo/src/lesson5_select/chan_range.go
one
two
go 計時器實例
我們經(jīng)常想在將來某個時間點執(zhí)行Go 代碼哥桥,或者在某個時間間隔重復(fù)執(zhí)行辙浑。 Go 內(nèi)置計時器和自動接收器功能使這兩項任務(wù)變得容易。我們先看看定時器泰讽,然后再看看行情例衍。
定時器代表未來的一個事件昔期。可告訴定時器您想要等待多長時間佛玄,它提供一個通道硼一,得到通知的時候執(zhí)行相應(yīng)程序。在這個示例中梦抢,計時器將等待2 秒鐘般贼。
<-timer1.C 阻塞定時器的通道 C ,直到它發(fā)送一個指示定時器超時的值奥吩。
如果只是想等待哼蛆,可以使用 time.Sleep 。定時器可能起作用的一個原因是在定時器到期之前取消定時器霞赫。
第一個定時器將在啟動程序后 2s 后過期腮介,但第二個定時器應(yīng)該在它到期之前停止。
package main
import (
"time"
"fmt"
)
func main(){
timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("Timer 1 expired")
timer2 := time.NewTimer(time.Second)
go func() {
<- timer2.C
fmt.Println("Timer 2 expired")
}()
stop2 := timer2.Stop()
if stop2{
fmt.Println("Timer 2 stoped")
}
}