速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd
速戰速決 go - go 高級: 線程間通信 channel/select
示例如下:
advanced/goroutine2.go
// go 高級 - 線程間通信 channel/select
// goroutine 相當於輕量級的線程(相對於傳統線程來說效率要高出很多),通過 go 關鍵字創建 goroutine,通過 channel 做 goroutine 間的通信
// 說明:
// 1、一個 goroutine 向 channel 發送數據,另一個 goroutine 從 channel 接收數據(這是要阻塞的,直到接收到數據爲止)
// 2、無緩衝區 channel 的意思是:寫數據和讀數據是一套動作,完成一套動作後才能執行下一套動作
// 向 channel 寫數據時,如果 channel 中有數據未讀,則是不能寫的,要阻塞着
// 3、有緩衝區 channel 的意思是:向緩衝區寫數據和從緩衝區讀數據是分別進行的,緩衝區是先入先出的
// 只要緩衝區不滿,即使沒人讀,也可以繼續向緩衝區寫數據,如果緩衝區滿了,就要阻塞了
package advanced
import (
"fmt"
"time"
)
func Goroutine2Sample() {
// 無緩衝區 channel 的 demo
goroutine2_sample1()
// 有緩衝區 channel 的 demo
goroutine2_sample2()
// select 的 demo
// 本例演示瞭如何通過 select 爲 channel 增加讀取超時的判斷
goroutine2_sample3()
}
func goroutine2_sample1() {
// 創建一個可以傳輸 int 類型數據的 channel(注:創建 channel 時必須指定傳輸的數據類型,每個 channel 只能傳輸你指定的數據類型)
var ch chan int = make(chan int)
// 創建一個只寫的 channel
// var ch_sendOnly chan<- int = make(chan int)
// var ch_sendOnly chan<- int = make(chan<- int)
// 創建一個只讀的 channel
// var ch_recvOnly <-chan int = make(chan int)
// var ch_recvOnly <-chan int = make(<-chan int)
go func() {
for i := 0; i < 5; i++ {
fmt.Println("準備寫入", i)
// 通過 channel<- 向 channel 發送數據(即寫數據)
// 如果 channel 中有未讀數據,則阻塞
ch <- i
fmt.Println("已經寫入", i)
}
// 關閉 channel
close(ch)
}()
// 通過 <-channel 從 channel 接收數據(即讀數據)
// 這是要阻塞的,直到讀出爲止
data := <-ch
fmt.Println("已經讀出", data)
// 遍歷 channel 中的數據
for data := range ch {
fmt.Println("已經讀出", data)
}
// 判斷 channel 是否關閉了(返回 false 則說明 channel 已經關閉了)
_, ok := <-ch
fmt.Println("channel狀態", ok)
// 我這裏某一次的運行結果如下:
/*
準備寫入 0
已經寫入 0
準備寫入 1
已經讀出 0
已經讀出 1
已經寫入 1
準備寫入 2
已經寫入 2
準備寫入 3
已經讀出 2
已經讀出 3
已經寫入 3
準備寫入 4
已經寫入 4
已經讀出 4
channel狀態 false
*/
}
func goroutine2_sample2() {
// 創建 channel 時指定緩衝區的大小(默認是無緩衝區的)
// 這個緩衝區的大小指的是可以保存的元素的數量
ch := make(chan int, 5)
go func() {
for i := 0; i < 5; i++ {
fmt.Println("準備寫入", i)
// 通過 channel<- 向緩衝區發送數據(即寫數據)
// 如果緩衝區滿了,則阻塞
ch <- i
fmt.Println("已經寫入", i)
}
// 關閉 channel
close(ch)
}()
time.Sleep(time.Second)
for i := 0; i < 5; i++ {
// 通過 <-channel 從緩衝區接收數據(即讀數據)
// 這是要阻塞的,直到讀出爲止
data := <-ch
fmt.Println("已經讀出", data, len(ch)) // len() 可以獲取 channel 的緩衝區中的元素的數量
}
// 判斷 channel 是否關閉了(返回 false 則說明 channel 已經關閉了)
_, ok := <-ch
fmt.Println("channel狀態", ok)
// 我這裏某一次的運行結果如下:
/*
準備寫入 0
已經寫入 0
準備寫入 1
已經寫入 1
準備寫入 2
已經寫入 2
準備寫入 3
已經寫入 3
準備寫入 4
已經寫入 4
已經讀出 0 4
已經讀出 1 3
已經讀出 2 2
已經讀出 3 1
已經讀出 4 0
channel狀態 false
*/
}
func goroutine2_sample3() {
ch := make(chan int)
quit := make(chan bool)
go func() {
for {
// select 中的 case 必須是讀 channel 或寫 channel
// 每次執行時,select 都會評估每個 case 語句
// 1、如果存在可以執行的 case(即沒有被阻塞),則從可以執行的 case 中隨機選擇一個執行
// 2、如果不存在可以執行的 case
// a) 有 default 時:執行 default
// b) 沒 default 時:阻塞,直到有 case 可以執行爲止
select {
case data := <-ch:
fmt.Println(time.Now(), "讀出", data)
case <-time.After(3 * time.Second):
fmt.Println(time.Now(), "從 ch 通道中獲取數據超時了,超時時間是 3 秒")
quit <- true
}
}
}()
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Second)
}
// 阻塞,直到從 quit 通道中收到數據爲止
<-quit
fmt.Println(time.Now(), "結束")
// 我這裏某一次的運行結果如下:
/*
2022-02-07 16:29:07.8439862 +0800 CST m=+0.006806901 讀出 0
2022-02-07 16:29:08.8481389 +0800 CST m=+1.010959601 讀出 1
2022-02-07 16:29:09.8511351 +0800 CST m=+2.013955801 讀出 2
2022-02-07 16:29:10.8637841 +0800 CST m=+3.026604801 讀出 3
2022-02-07 16:29:11.8757439 +0800 CST m=+4.038564601 讀出 4
2022-02-07 16:29:14.8760507 +0800 CST m=+7.038871401 從 ch 通道中獲取數據超時了,超時時間是 3 秒
2022-02-07 16:29:14.8760507 +0800 CST m=+7.038871401 結束
*/
}
速戰速決 go https://github.com/webabcd/GoSample
作者 webabcd