go——select

在go的語言規範中,select中的case的執行順序是隨機的,當有多個case都可以運行時,select會隨機公平地選出一個執行,其它的便不會執行

package main

import "fmt"

func main() {
    ch := make (chan int, 1)

    ch<-1
    select {
    case <-ch:
        fmt.Println("隨機一")
    case <-ch:
        fmt.Println("隨機二n")
    }
}

輸出內容爲隨機一二里面的任意一個。case後面必須是channel操作,否則報錯;default子句總是可運行的,所以沒有default的select纔會阻塞等待事件;沒有運行的case,那麼將會阻塞事件發生報錯(死鎖)

二、select的應用場景

1.timeout超時判斷

package main

import (
    "fmt"
    "time"
)

func main() {
    timeout := make (chan bool, 1)
    go func() {
        time.Sleep(1*time.Second) // 休眠1s,如果超過1s還沒I操作則認爲超時,通知select已經超時啦~
        timeout <- true
    }()
    ch := make (chan int)
    select {
    case <- ch:
    case <- timeout:
        fmt.Println("超時啦!")
    }
}

也可以這麼寫

package main

import (
    "fmt"
    "time"
)

func main() {
    timeout := make (chan bool, 1)
    go func() {
        time.Sleep(1*time.Second) // 休眠1s,如果超過1s還沒I操作則認爲超時,通知select已經超時啦~
        timeout <- true
    }()
    ch := make (chan int)
    select {
    case <- ch:
    case <- timeout:
        fmt.Println("超時啦!")
    }
}

2.判斷channel是否阻塞(或者說channel是否已經滿了)

package main

import (
    "fmt"
)

func main() {
    ch := make (chan int, 1)  // 注意這裏給的容量是1
    ch <- 1
    select {
    case ch <- 2:
    default:
        fmt.Println("通道channel已經滿啦,塞不下東西了!")
    }
}

3.退出機制

package main

import (
    "fmt"
    "time"
)

func main() {
    i := 0
    ch := make(chan string, 0)
    defer func() {
        close(ch)
    }()

    go func() {
        DONE: 
        for {
            time.Sleep(1*time.Second)
            fmt.Println(time.Now().Unix())
            i++

            select {
            case m := <-ch:
                println(m)
                break DONE // 跳出 select 和 for 循環
            default:
            }
        }
    }()

    time.Sleep(time.Second * 4)
    ch<-"stop"
}

三、select的實現

select-case的channel操作編譯成了if-else,如:

select{
case v := <-c:
  ...foo
default:
  ...bar
}

會被編譯爲

if selectnbrecv(&v, c) {
   ...foo
} else {

   ..bar
}

類似地

select {
case v, ok := <-c:
   ...foo
default:
   ...bar
}

會被編譯爲

if c!=nil && selectnbrecv2(&v, &ok, c) {
   ...foo
} else {
   ...bar
}

。、、、、、、、、

、、、、、

 

三、select死鎖

select不注意也會導致goruntine掛起

func main() {
    ch := make(chan string)
    select {
        case <-ch:
    }
}

 

package main
func main() {
    select{}
}

上面兩種情況都會導致goruntine掛起,不過因爲是在main函數裏而且只有一個goroutine,所以會直接panic

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章