day14 channel通道

染指未来發表於2024-07-03

瞭解channel通道

  • 用於 協程(執行緒間的通訊)
  • 一個通道傳送和接收資料,預設是 阻塞
  • 宣告通道
    • 使用 make 宣告通道
    • 使用 var 定義 通道後,在使用make建立通道
  • 不要透過共享記憶體實現通訊,要透過通訊實現共享記憶體
package main

import "fmt"

func main() {
	/* channel
	- 用於 協程(執行緒間的通訊)
	- 一個通道傳送和接收資料,預設是 `阻塞`
	- 宣告通道
		- 使用 make 宣告通道
		- 使用 var 定義 通道後,在使用make建立通道
	- 不要透過共享記憶體實現通訊,要透過通訊實現共享記憶體
	*/

	// 1. 使用make 宣告通道
	channel01 := make(chan int)

	// 2. var 宣告 通道,
	var channel02 chan bool
	channel02 = make(chan bool)

	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println("goroutine")
		}

		// 2. 往通道里放值 使用 <-
		channel01 <- 10
		channel02 <- true
	}()

	// 3. 當有通道在取值時,預設阻塞
	channelData1 := <-channel01
	channelData2 := <-channel02
	fmt.Println("獲取通道1的資料:", channelData1)
	fmt.Println("獲取通道2的資料:", channelData2)
}

channel死鎖 場景

  • 建立了通道 ,沒有寫入資料 【死鎖】
  • 建立了通道,寫入資料,沒人接收 【死鎖】
  • 兩個通道相互依賴 【死鎖】 channelA <==> channelB

通道關閉

  • 通道不再傳送資料了,就手動關閉
package main

import (
	"fmt"
	"time"
)

func closeChannelFunc(ch chan int) {
	for i := 0; i < 10; i++ {

		ch <- i
	}
	close(ch)
}

func main() {
	/* 通道的關閉
	- 通道不再傳送資料了,就手動關閉
	*/

	closeCh := make(chan int)
	go closeChannelFunc(closeCh)
	for {
		time.Sleep(time.Second)
		// ok 判斷chan的狀態是否關閉。如果關閉了,不會在取值
		data, ok := <-closeCh
		if ok == false {
			fmt.Println("通道已經讀取完畢!", ok)
			break
		}
		fmt.Println("正在讀取通道中的資料:", data)
	}
}

for 遍歷通道

package main

import (
	"fmt"
	"time"
)

func addDataChainFunc(ch chan int) {
	for i := 0; i < 10; i++ {
		ch <- i
		time.Sleep(time.Second)
	}
	close(ch)
}
func main() {
	chan02 := make(chan int)
	go addDataChainFunc(chan02)
	for data := range chan02 {
		fmt.Println("for range 遍歷通道資料", data)
	}
}

緩衝通道

  • 遵循:先進先出規則
  • 緩衝通道: 通道又一個引數定義緩衝去,傳送資料直到緩衝區填滿位置,才會被接受。 只有緩衝區清空才會阻塞
    • 定義: make(chain int , 6)
  • 非緩衝通道: chan 只能存放一個資料,傳送和接收都是阻塞。一次傳送一次接收
    • 定義: make(chai int , 1) // 預設是:1
package main

import (
	"fmt"
	"strconv"
	"time"
)

func addDataChanFunc(ch chan string) {
	for i := 0; i < 10; i++ {
		ch <- strconv.Itoa(i)
		fmt.Println("子Goroutine 放入資料:", i)
		time.Sleep(time.Second)
	}
	close(ch)
}

func main() {
	/*
		通道
			- 遵循:先進先出規則
			- 緩衝通道:  通道又一個引數定義緩衝去,傳送資料直到緩衝區填滿位置,才會被接受。 只有緩衝區清空才會阻塞
				- 定義: make(chain int , 6)
			- 非緩衝通道: chan 只能存放一個資料,傳送和接收都是阻塞。一次傳送一次接收
				- 定義: make(chai int , 1)  // 預設是:1
	*/

	// 非緩衝 通道
	unbufferedChannel := make(chan int)
	fmt.Printf("非緩衝通道:型別:%T 容量:%d 長度:%d \n", unbufferedChannel, cap(unbufferedChannel), len(unbufferedChannel)) //非緩衝通道:型別:chan int 容量:0 長度:0

	// 緩衝通道. 放入資料不會產生死鎖。需要等另外的執行緒來拿資料
	bufferChannel := make(chan string, 5)
	bufferChannel <- "aaa"
	fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0
	bufferChannel <- "bbb"
	fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0
	bufferChannel <- "ccc"
	fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0
	bufferChannel <- "ddd"
	fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0
	bufferChannel <- "eee"
	fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0

	//// 超出緩衝通道的容量。產生死鎖 deadlock!
	// bufferChannel <- "fff"
	// fmt.Printf("緩衝通道:型別:%T 容量:%d 長度:%d \n", bufferChannel, cap(bufferChannel), len(bufferChannel)) // 緩衝通道:型別:chan int 容量:5 長度:0
	fmt.Println("****************")
	bufferChan := make(chan string, 4) // 讀的量和取存在多元化。寫入時間取決於讀的時間! 4 表示一次寫4個到通道
	go addDataChanFunc(bufferChan)
	for data := range bufferChan {
		time.Sleep(1 * time.Second)
		fmt.Println("main 讀取 bufferChan 中的資料:", data)
	}
	fmt.Println("Main end")

}

定向通道

  • 雙向通道: 邊寫邊讀
  • 單項通道: 只讀/只寫
package main

import (
	"fmt"
	"sync"
)

func writeOnlyChan(ch chan<- int) {

	// 單向通道:只寫操作
	data := 1
	ch <- data
	fmt.Println("只寫通道:", data)
	wg.Done()

}
func readOnlyChan(ch <-chan int) int {

	// 單向通道:只寫操作
	data := <-ch
	fmt.Println("只讀通道:", data)
	wg.Done()
	return data

}

// 定義 同步等待組
var wg sync.WaitGroup

func main() {
	/*
		定向通道:
			- 雙向通道: 邊寫邊讀
			- 單項通道: 只讀/只寫
	*/
	// 雙向通道:讀寫
	readWriteChan := make(chan int, 1)
	readWriteChan <- 1
	data := <-readWriteChan
	fmt.Println(data)

	// 單項通道
	//// writeChan := make(chan<- int) // 只寫 : 一般用於引數場景。函式引數,防止通道濫用
	//// readChan := make(<-chan int)  // 只讀:  一般用於引數場景。函式引數,防止通道濫用

	chan01 := make(chan int)
	wg.Add(2)
	go writeOnlyChan(chan01)
	go readOnlyChan(chan01)
	wg.Wait()

}

Select 和 channel 使用

package main

import (
	"fmt"
	"time"
)

func main() {
	/* 通道中的select */
	ch1 := make(chan int)
	ch2 := make(chan int)
	go func() {
		time.Sleep(1 * time.Second * 2)
		ch1 <- 1
	}()
	go func() {
		time.Sleep(1 * time.Second * 2)
		ch2 <- 109
	}()

	// 讀取chan中的資料。無論誰先放入。就使用誰的資料,其他的拋棄
	// select 和 switch 。 只是在通道中使用,case表示式需要一個通道結果
	select {
	case num1 := <-ch1: // 需要一個具體的結果,即從通道中取出來的資料
		fmt.Println("num1:", num1)
	case num2 := <-ch2:
		fmt.Println("num2:", num2)
		//default:
		//	fmt.Println("沒拿到通道中的資料!")
	}
}

Timer定時器和 channel 通道

package main

import (
	"fmt"
	"time"
)

func main() {
	/* Timer 定時器 */
	// 建立一個定時器
	//  C <-chan Time 。 只讀通道,這個通道中存放的值就是 NewTimer 傳入的時間
	timerObj := time.NewTimer(time.Second * 3)

	//當前時間
	fmt.Println(time.Now())
	timeChan := timerObj.C
	fmt.Println(<-timeChan) // 獲取 定時器物件中的時間

}

相關文章