了解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) // 获取 定时器对象中的时间
}