欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 能源 > Go语言 管道1

Go语言 管道1

2024/11/30 20:40:19 来源:https://blog.csdn.net/json_ligege/article/details/142132540  浏览:    关键词:Go语言 管道1

本篇文章主要介绍Go语言 无缓冲管道和有缓冲管道概念,特点及其使用示例。

目录

无缓冲通道

有缓冲的管道

语法

特点

代码示例

未分配空间示例

读取次数不一致示例

For-range遍历

总结


无缓冲通道

sync.RWMutex{}

当涉及到多go程时,c语言使用互斥量,上锁来保持资源同步,免资源竞争问题

go语言也支持这种方式,但是go语言更好的解决方案是使用管道、通道

使用管道不需要我们去进行加解锁

A 往管道里面写数据 B从管道里面读数据,go自动帮我们做好了数据同步。

示例如下:

package mainimport ("fmt""time"
)func main() {// 创建管道:创建一个装数字的管道 ==》 channel// strChan := make(chan string) // 装字符串的管道// 装数字的管道,使用管道的时候一定要make,同map一样,否则nil// 此时是无缓冲的管道numChan := make(chan int)// 创建两个go程,父写数据,子读数据go func() {for i := 0; i < 50; i++ {data := <-numChanfmt.Println("data:", data)}}()for i := 0; i < 50; i++ {// 向管道中写入数据numChan <- ifmt.Println("===> 主go程,写入数据:", i)}time.Sleep(5 * time.Second)
}

运行结果:

data: 0
===> 主go程,写入数据: 0
===> 主go程,写入数据: 1
data: 1
data: 2
===> 主go程,写入数据: 2
===> 主go程,写入数据: 3
data: 3
data: 4
===> 主go程,写入数据: 4
===> 主go程,写入数据: 5
data: 5
data: 6
===> 主go程,写入数据: 6
===> 主go程,写入数据: 7

写入和读取无规律,可能读在写的前面打印出来。

有缓冲的管道

语法

numsChan := make(chan int,10)

特点

1.当缓冲写满的时候,写阻塞,当被读取后,再恢复写入

2.当缓冲区读取完毕,读阻塞

3.如果管道没有使用make分配空间,那么管道默认是ni1的,读取、写入都会阻塞

4.对一个管道,读与写次数必须对等

代码示例

通过创建一个数字管道,主go程和一个子go程写入数据,另一个子go程读取数据。

需要注意写入数据的总条数与读取数据的总条数相等。

示例如下:

package mainimport ("fmt""time"
)func main() {// 有缓冲的管道numChan := make(chan int, 10)go func() {for i := 0; i < 50; i++ {// 从管道中读取数据data := <- numChanfmt.Println("子go程1 读取数据 ==》 data:", data)}}()go func() {for i := 0; i < 20; i++ {// 从管道中读取数据numChan <- ifmt.Println("子go程2 写入数据:", i)}}()for i := 20; i < 50; i++ {// 向管道中写入数据numChan <- ifmt.Println("===> 主go程,写入数据:", i)}time.Sleep(5 * time.Second)
}

运行结果:

===> 主go程,写入数据: 20
===> 主go程,写入数据: 21
===> 主go程,写入数据: 22
===> 主go程,写入数据: 23
===> 主go程,写入数据: 24
===> 主go程,写入数据: 25
子go程2 写入数据: 0
子go程2 写入数据: 1
子go程2 写入数据: 2
子go程2 写入数据: 3
===> 主go程,写入数据: 26
子go程1 读取数据 ==》 data: 20
子go程1 读取数据 ==》 data: 0
子go程1 读取数据 ==》 data: 21
子go程1 读取数据 ==》 data: 22
子go程1 读取数据 ==》 data: 23
子go程1 读取数据 ==》 data: 24
子go程1 读取数据 ==》 data: 25
子go程1 读取数据 ==》 data: 26
子go程1 读取数据 ==》 data: 1
子go程1 读取数据 ==》 data: 2

 

未分配空间示例

如果管道不分配空间直接使用,会怎么样呢?

示例如下:

package mainimport ("fmt""time"
)func main() {var names chan string // 默认是nil的go func() {fmt.Println("names:", names)}()names <- "hello world"time.Sleep(1 * time.Second)
}

运行结果:

$ go run 不分配空间.go
names: <nil>
fatal error: all goroutines are asleep - deadlock!

解决方法进行分配空间

示例如下:

package mainimport ("fmt""time"
)func main() {//var names chan string // 默认是nil的names := make(chan string, 10)go func() {fmt.Println("names:", names)}()names <- "hello world" // 由于names是nil的,写操作会阻塞在这里time.Sleep(1 * time.Second)
}

 

读取次数不一致示例

读,当主程序被管道阻塞时,那么程序将锁死崩溃

要求我们一定要读写次数保持一致

示例如下:

package mainimport ("fmt""time"
)func main() {numsChan1 := make(chan int, 20)//写go func() {for i := 0; i < 15; i++ {// 向管道中写入数据numsChan1 <- ifmt.Println("===> 子go程,写入数据:", i)}}()// 读for i := 0; i < 20; i++ {// 从管道中读取数据data := <- numsChan1fmt.Println("主go程 读取数据 ==》 data:", data)}time.Sleep(5 * time.Second)
}

当管道的读写次数不一致的时候

如果阻塞在主go程,那么程序会崩溃

如果阻塞在子go程,那么会出现内存泄漏

For-range遍历

避免出现读写不一致,直接使用for-range写法。

示例如下:

numsChan2 := make(chan int, 20)//写
go func() {for i := 0; i < 15; i++ {// 向管道中写入数据numsChan2 <- ifmt.Println("===> 子go程,写入数据:", i)}fmt.Println("数据全部写入完毕,准备关闭管道")close(numsChan2)
}()// 读
// 遍历管道时,只返回一个值
// for range并不知道管道是否已经写完了,所以会一直在这里等待
// 在写入端,将管道关闭,for range遍历关闭管道时,会退出
for v := range numsChan2{fmt.Println("读取数据:", v)
}time.Sleep(5 * time.Second)

总结

1.当管道写满了,写阻塞;

2.当缓冲区读完了,读阻塞;

3.如果管道没有使用make分配空间,管道默认是nil;

4.从nil管道读取数据,写入数据,都会阻塞(注意,不会崩溃);

5.从一个已经colse的管道读取数据时,会返回零值(不会崩溃);

6.像一个已经colse的管道写数据时,会崩溃;

7.关闭一个已经colse的管道,程序会崩溃;

8.关闭管道的动作,一定要在写端执行,不应该放到读端,否则写的继续写会崩溃;

9.读和写的次数一定要对等,否则:

        在多个go程中:资源泄露;

        在主go程中,程序崩溃(deadlock)。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com