速戰速決 go - go 高級: 線程間通信 channel/select

速戰速決 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

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