平時我們經常會用 for 和 select 語句來搭配使用來實現不斷的通訊。
比如一個 goroutine 不斷的從管道中發送字符,另一個不斷的接收字符,當在管道關閉後,正常結束程序的執行。
如果我們不使用類似break, goto 等配合跳出到指定的標籤,那麼程序就不好控制正常結束了。
這裏簡要說明break,goto配合標籤使用時的執行邏輯如下:
break是跳出到標籤對應的當前指令,然後執行其下一條指令。
goto是跳往將要執行的指令。
goto + 標籤
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
intChan <- i
}
defer close(intChan)
}()
for {
select {
case i, ok := <-intChan:
if !ok {
fmt.Println("in break")
goto LOOP
}
fmt.Println(i)
case <-time.After(500 * time.Millisecond):
fmt.Println("time expired")
}
}
LOOP:
fmt.Println("end")
}
break + 標籤
對於上述代碼,如果我們想要使用break來改寫,則如下
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
intChan <- i
}
defer close(intChan)
}()
for {
select {
case i, ok := <-intChan:
if !ok {
fmt.Println("in break")
break LOOP
}
fmt.Println(i)
case <-time.After(500 * time.Millisecond):
fmt.Println("time expired")
}
}
LOOP:
fmt.Println("end")
}
但這樣是不會通過編譯的,顯示如下:
# command-line-arguments
./breakloop.go:23:11: break label not defined: LOOP
./breakloop.go:30:1: label LOOP defined and not used
爲什麼呢?因爲break這個不是goto,幾乎可以到達程序的任何一個地方。它只能在其定義了後,纔可以使用。
現在將其位置調整成如下,即可正常工作。
package main
import (
"fmt"
"time"
)
func main() {
intChan := make(chan int)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(time.Second)
intChan <- i
}
defer close(intChan)
}()
LOOP:
for {
select {
case i, ok := <-intChan:
if !ok {
fmt.Println("in break")
break LOOP
}
fmt.Println(i)
case <-time.After(500 * time.Millisecond):
fmt.Println("time expired")
}
}
fmt.Println("end")
}
上面的LOOP標籤,就指代了當前的for{}語句,當用break LOOP時,其會跳過當前
語句(for{}
),而開始執行下一條
語句(也即 fmt.Println("end")
)
如此,則滿足我們需求了。如果break時不帶標籤,那麼其只退出當前的 select語句,無法跳出 for,就永遠不會結束了。
總之,break LOOP,需要出現在LOOP所標誌的那條語句中。