Go教程-select进阶|Go主题月

469 阅读2分钟

本文为翻译文章

原文地址:golangbot.com/select/

死锁和 default

package main

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

在上面的程序中,我们创建了一个 ch 通道。我们尝试从 select 语句里面读取 ch 通道。select 语句将永远阻塞,因为没有其他 Goroutine 正在写入此通道,因此将导致死锁。该程序将在运行时发生 panic

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:  
main.main()  
    /tmp/sandbox627739431/prog.go:6 +0x4d

如果存在 default,则不会发生此死锁,因为将在没有其他情况准备好时执行 default。

上面的程序用下面的 default 重写:

package main

import "fmt"

func main() {  
    ch := make(chan string)
    select {
    case <-ch:
    default:
        fmt.Println("default case executed")
    }
}

程序输出:

default case executed

同样,即使选择只 nil 通道,也会执行default。

package main

import "fmt"

func main() {  
    var ch chan string
    select {
    case v := <-ch:
        fmt.Println("received value", v)
    default:
        fmt.Println("default case executed")

    }
}

在上面的程序中,ch 通道的值为 nil,我们尝试从 select 中读取 ch 通道里的数据。如果 default 不存在,则该 select 将永远受阻并造成死锁。由于我们在选择框内有一个 default,它将被执行并且程序将打印出来:

default case executed  

随机选择

当 select 语句中的多个 case 准备就绪时,将随机执行其中之一。

package main

import (  
    "fmt"
    "time"
)

func server1(ch chan string) {  
    ch <- "from server1"
}
func server2(ch chan string) {  
    ch <- "from server2"

}
func main() {  
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)
    time.Sleep(1 * time.Second)
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
}

在上面的程序中,调用了 server1 和 server Goroutine。然后,主程序休眠 1 秒钟。当到达 select 语句时。server1 已经写入字符串 from server1output1 通道,server2 已经写入字符串 from server2output2通道,因此这两种情况下都准备好执行。如果您多次运行此程序,则输出将在随机选择的情况之间 from server1from server2 取决于选择的情况而有所不同。

陷阱 — 空 select

package main

func main() {  
    select {}
}

您认为上面程序的输出是什么?

我们知道 select 语句将阻塞直到执行其中一种情况。在这种情况下,select 语句没有任何情况,因此它将永远阻塞,从而导致死锁。该程序将发生 panic:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:  
main.main()  
    /tmp/sandbox246983342/prog.go:4 +0x25