持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情
3. 无缓冲管道channel
sync.RWMutex{}
-
当涉及到多go程时,c语言使用互斥量,上锁来保持资源同步,避免资源竞争问题
-
go语言也支持这种方式,但是go语言更好的解决方案是使用管道、通道 channel
-
使用通道不需要我们去进行加解锁
-
A 往通道里面写数据 B从管道里面读数据,go自动帮我们做好了数据同步
-
装数字的管道,使用管道的时候一定要make, 同map一样,否则是nil
-
此时是无缓冲的管道
-
创建管道: 创建一个装数字的管道 ==> channel
package main
import (
"fmt"
"time"
)
func main() {
//strChan := make(chan string) //装字符串的管道
//make(map[int]string, 10)
//numChan := make(chan int)
//有缓冲的管道
numChan := make(chan int, 10)
//创建两个go程,父亲写数据,儿子读数据
go func() {
for i := 0; i < 50; i++ {
data := <-numChan
fmt.Println("子go程1 读取数据 ===》 data:", data)
}
}()
go func() {
for i := 0; i < 20; i++ {
//向管道中写入数据
numChan <- i
fmt.Println("子go程2 写入数据:", i)
//time.Sleep(1 * time.Second)
}
}()
for i := 20; i < 50; i++ {
//向管道中写入数据
numChan <- i
fmt.Println("======> 这是主go程, 写入数据:", i)
//time.Sleep(1 * time.Second)
}
time.Sleep(5 * time.Second)
}
4. 有缓冲区管道
numsChan := make(chan int, 10)
-
当缓冲写满的时候,写阻塞,当被读取后,再恢复写入
-
当缓冲区读取完毕,读阻塞
-
如果管道没有使用make分配空间,那么管道默认是nil的,读取、写入都会阻塞
-
对于一个管道,读与写的次数,必须对等
package main
import (
"fmt"
"time"
)
func main() {
var names chan string //默认是nil的
names = make(chan string, 10)
go func() {
fmt.Println("names:", <-names)
}()
names <- "hello" //由于names是nil的,写操作会阻塞在这里
time.Sleep(1 * time.Second)
numsChan1 := make(chan int, 10)
//写
go func() {
for i := 0; i < 50; i++ {
numsChan1 <- i
fmt.Println("写入数据:", i)
}
}()
//读,当主程序被管道阻塞时,那么程序将锁死崩溃
//要求我们一定要读写次数保持一致
func() {
for i := 0; i < 60; i++ {
fmt.Println("主程序准备读取数据.....")
data := <-numsChan1
fmt.Println("读取数据:", data)
}
}()
for {
;
}
}
- 当管道的读写次数不一致的时候
- 如果阻塞在主go程,那么程序会崩溃
- 如果阻塞在子go程,那么会出现内存泄露
5. for range遍历
package main
import "fmt"
func main() {
numsChan2 := make(chan int, 10)
//写
go func() {
for i := 0; i < 50; i++ {
numsChan2 <- i
fmt.Println("写入数据:", i)
}
fmt.Println("数据全部写完毕,准备关闭管道!")
close(numsChan2)
}()
//遍历管道时,只返回一个值
//for range是不知道管道是否已经写完了,所以会一直在这里等待
//在写入端,将管道关闭,for range遍历关闭的管道时,会退出
for v := range numsChan2 {
fmt.Println("读取数据 :", v)
}
fmt.Println("OVER!")
}