瞭解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)
}
}
緩衝通道
- 遵循:先進先出規則
- 緩衝通道: 通道又一個引數定義緩衝去,傳送資料直到緩衝區填滿位置,才會被接受。 只有緩衝區清空才會阻塞
- 非緩衝通道: 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) // 獲取 定時器物件中的時間
}