8.14GO之條件語句
Go語言條件語句
一些大題的和類C語言無異,但是:
-
Go 沒有三目運算子,所以不支援 ?: 形式的條件判斷。
相當於Java中的:
public boolean isEmpty(){
return size == 0 ? true : false;
}
Go之條件語句
語句 | 描述 |
---|---|
[if 語句] |
if 語句 由一個布林表示式後緊跟一個或多個語句組成。 |
[if...else 語句] |
if 語句 後可以使用可選的 else 語句, else 語句中的表示式在布林表示式為 false 時執行。 |
[if 巢狀語句] |
你可以在 if 或 else if |
[switch 語句] |
switch 語句用於基於不同條件執行不同動作。 |
[select 語句] |
select 語句類似於 switch 語句,但是select會隨機執行一個可執行的case。如果沒有case可執行,它將阻塞,直到有case可執行。 |
Go條件語句之select
語句
簡單理解:
-
chan關鍵字定義了goroutine中的管道通訊,一個goroutine可以和另一個goroutine進行通訊。
什麼是goroutine
goroutine是Go中最基本的執行單元。
特點:
-
每一個Go程式至少有一個Goroutine:主Goroutine。
執行緒(Thread)
特點:
-
輕量級程序(Lightweight Process,LWP)
-
程式執行流的最小單元
一個標準執行緒的組成:
-
執行緒ID
-
當前指令指標(PC)
-
暫存器集合
-
堆
-
棧
一個執行緒的特點:
-
是程序中的一個實體,是被系統獨立排程和分派的基本單位
-
多執行緒之間 共享堆,不共享棧。 執行緒切換由作業系統排程
協程(coroutine)
特點:
-
稱微執行緒與子例程(或者稱為函式)
-
是一種程式元件。相對子例程而言,協程更為一般和靈活,但在實踐中使用沒有子例程那樣廣泛
一個協程的特點:
-
多協程之間 共享堆,不共享棧
執行緒與協程的不同點
-
執行緒切換由作業系統排程
-
協程的切換由程式設計師在程式碼中顯示控制
好處:
-
避免了上下文切換的額外耗費
-
兼顧了多執行緒的優點
-
簡化了高併發程式的複雜
Goroutine--->需要深入瞭解、實踐。目前還不是很明白。
特點:
-
協程不是併發的,而Goroutine支援併發的。
-
Goroutine可以理解為一種Go語言的協程。
-
同時它可以執行在一個或多個執行緒上。-
-
goroutine支援協程之間的互相通訊--->使用關鍵字
chan
是channel
的簡寫
channel
的特點
特點:
-
一個 channels 是一個通訊機制,可以讓一個 goroutine 通過它給另一個 goroutine 傳送值資訊。
-
每個 channel 都有一個特殊的型別,也就是 channels 可傳送資料的型別。一個可以傳送 int 型別資料的 channel 一般寫為 chan int。
和多執行緒ThreadLocal不一樣的地方:
ThreadLocal:
-
ThreadLocal能夠放一個執行緒級別的變數
-
本身能夠被多個執行緒共享使用,又能達到執行緒安全的目的
-
在多執行緒環境下保證成員變數的安全
ThreadLocal相當於一個共享的記憶體區域,供多個執行緒共享堆的資料。執行緒只能共享堆的資料,不能共享棧的資料。
Goroutine:
-
Go語言提倡使用通訊的方法代替共享記憶體
-
資源需要在 goroutine 之間共享時,通道在 goroutine 之間架起了一個管道,並提供了確保同步交換資料的機制。
-
因為go是強型別語言,所以宣告通道時,需要指定將要被共享的資料的型別。
-
可以通過通道共享goroutine當中的內建型別、命名型別、結構型別和引用型別的值或者指標。
可以說goroutine實現了協程之間的棧的資料的共享。
通訊的方法就是使用通道(channel),如下圖所示:
channel 是一種佇列一樣的結構。
channel
規則的特點:
-
可以向一個
goroutine
建立多個channel
通道 -
同時只能有一個 goroutine 訪問通道進行傳送和獲取資料。
-
goroutine遵循先入先出(First In First Out)的規則,保證收發資料的順序。--->和棧空間的規則一樣
通道的使用方式
宣告通道
建立通道
使用通道傳送資料
通道本身需要一個型別進行修飾,就像切片型別需要標識元素型別。通道的元素型別就是在其內部傳輸的資料型別
宣告通道型別格式:
var 通道變數 chan 通道型別
-
通道型別:通道內的資料型別。
-
通道變數:儲存通道的變數。--->通道本身也是一個變數,
chan 型別的空值是 nil,聲明後需要配合 make 後才能使用。
建立通道格式--->通道本身是引用型別,需要使用make進行建立
通道例項 := make(chan 資料型別)
-
資料型別:通道內傳輸的元素型別。
-
通道例項:通過make建立的通道控制代碼。--->可以理解為在堆中開闢一個空間
示例:
package main
import (
"expvar"
"fmt"
)
func main() {
ch1 := make(chan int) //建立一個整數型別的通道
ch2 := make(chan interface{}) //建立一個空介面型別的通道, 可以存放任意格式
type Equip struct {
expvar.Var
}
ch2 := make(chan *Equip)
fmt.Println()
}
使用通道傳送資料--->操作符:<-
通道傳送蘇劇的格式:
通道變數 <- 值
-
通道變數:通過make建立好的通道例項。--->這是一個堆裡面的實際物件,對應到一個堆的地址
-
值:可以是變數、常量、表示式或者函式返回值等。值的型別必須與ch通道的元素型別一致。
示例:
package main
func main() {
ch1 := make(chan int) //建立一個整數型別的通道
ch2 := make(chan interface{}) //建立一個空介面型別的通道, 可以存放任意格式
ch1 <- 1
ch2 <- 3.1415926
ch2 <- "Hello,World!"
}
使用通道接受資料--->操作符:<-
通道接收資料的特徵:
-
通道的收發操作在不同的兩個 goroutine 間進行。
-
通道的資料在沒有接收方處理時,資料傳送方會持續阻塞,因此通道的接收必定在另外一個 goroutine 中進行。
-
-
接收將持續阻塞直到傳送方傳送資料。
-
接收方接收時,通道中沒有傳送方傳送資料,接收方也會發生阻塞,直到傳送方傳送資料為止。
-
-
每次接收一個元素。
-
通道一次只能接收一個數據元素。
-
概括:
-
通過通道傳送資料需要兩個goroutine
-
傳送沒有接收會阻塞goroutine,接收沒有傳送資料也會阻塞goroutine--->非常類似Java多執行緒中的執行緒通訊模型
-
可以建立非阻塞接收資料的型別
接收資料的四種寫法:
- 阻塞式接收資料
- 非阻塞式接收資料
- 接收任意資料,忽略接收的資料
- 迴圈接收
阻塞式接收資料:
data <- ch
執行該語句時將會阻塞,直到接收到資料並賦值給 data 變數。
非阻塞式接收資料:
data, ok <- ch
-
data:表示接收到的資料。未接收到資料時,data 為通道型別的零值。
-
ok:表示是否接收到資料。
非阻塞的通道接收方法可能造成高的 CPU 佔用,因此使用非常少。如果需要實現接收超時檢測,可以配合 select 和計時器 channel 進行
接收任意資料,忽略接收的資料:
<- ch
阻塞接收資料後,忽略從通道返回的資料。執行該語句時將會發生阻塞,直到接收到資料,但接收到的資料會被忽略。
通過通道在 goroutine 間阻塞收發實現併發同步。
使用通道做同步併發的例項:
package main
import "fmt"
func main() {
//構建一個通道
ch := make(chan int)
//開啟一個併發匿名函式
go func() {
fmt.Println("start goroutine")
//通過通道通知main的goroutine
ch <- 0
fmt.Println("exit goroutine")
}()
fmt.Println("wait goroutine")
//等待匿名goroutine
<- ch
fmt.Println("all done")
}
可以看到執行順序:
-
先執行main中的構建和通道接收的內容。到
<- ch
此時goroutine進入阻塞狀態 -
接著等到
go func()
當中的goroutine執行向通道ch
中新增值 -
最後
main
中收到了值以後解除阻塞狀態,往下執行
所以輸出順序是:
wait goroutine
start goroutine
exit goroutine
all done
程式碼說明:
ch := make(chan int) //構建一個同步用的通道。
go func() //開啟一個匿名函式的併發。
ch <- 0 //匿名 goroutine 即將結束時,通過通道通知 main 的 goroutine,這一句會一直阻塞直到 main 的 goroutine 接收為止。
<- ch //開啟 goroutine 後,馬上通過通道等待匿名 goroutine 結束。
雖然是併發執行但是通過阻塞的方式還是可以實現資料的安全、準確性
迴圈接收:
借用 for range 語句進行多個元素的接收操作
for data := range ch {
}
特點:
-
通道 ch 可以進行遍歷--->遍歷的結果就是接收到的資料。資料型別就是通道的資料型別。
-
通過 for 遍歷獲得的變數只有一個,即 data。
有點類似增強for迴圈
示例:
package main
import (
"fmt"
"time"
)
func main() {
//構建一個通道
chNo2 := make(chan int)
//開啟一個併發匿名函式
go func() {
//for迴圈,從3->0
for i := 3; i > 0; i-- {
//每次將迴圈的值傳送到main的goroutine中
chNo2 <- i
//每次傳送完時等待
time.Sleep(time.Second)
}
}()
//遍歷接收通道的資料
for data := range chNo2 {
//列印通道資料
fmt.Println(data)
//當遇到資料0時,退出接收迴圈
if data == 0 {
break
}
}
}
程式碼說明:
ch := make(chan int)
//通過 make 生成一個整型元素的通道。
go func() //將匿名函式併發執行。
ch <- i //將 3 到 0 之間的數值依次傳送到通道 ch 中。
for data := range ch //使用 for 從通道中接收資料。
本篇內容參考:
It's a lonely road!!!