Go语言 管道1

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

目录

无缓冲通道

有缓冲的管道

语法

特点

代码示例

未分配空间示例

读取次数不一致示例

For-range遍历

总结


无缓冲通道

sync.RWMutex{}

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

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

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

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

示例如下:

Go 复制代码
package main

import (
   "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 := <-numChan
         fmt.Println("data:", data)
      }
   }()

   for i := 0; i < 50; i++ {
      // 向管道中写入数据
      numChan <- i
      fmt.Println("===> 主go程,写入数据:", i)
   }

   time.Sleep(5 * time.Second)
}

运行结果:

bash 复制代码
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程读取数据。

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

示例如下:

Go 复制代码
package main

import (
   "fmt"
   "time"
)

func main() {
   // 有缓冲的管道
   numChan := make(chan int, 10)

   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)
      }
   }()

   for i := 20; i < 50; i++ {
      // 向管道中写入数据
      numChan <- i
      fmt.Println("===> 主go程,写入数据:", i)
   }

   time.Sleep(5 * time.Second)
}

运行结果:

bash 复制代码
===> 主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

未分配空间示例

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

示例如下:

Go 复制代码
package main

import (
   "fmt"
   "time"
)

func main() {
   var names chan string // 默认是nil的
   go func() {
      fmt.Println("names:", names)
   }()

   names <- "hello world"
   time.Sleep(1 * time.Second)
}

运行结果:

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

解决方法进行分配空间

示例如下:

Go 复制代码
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 world" // 由于names是nil的,写操作会阻塞在这里
   time.Sleep(1 * time.Second)
}

读取次数不一致示例

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

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

示例如下:

Go 复制代码
package main

import (
   "fmt"
   "time"
)

func main() {

   numsChan1 := make(chan int, 20)

   //写
   go func() {
      for i := 0; i < 15; i++ {
         // 向管道中写入数据
         numsChan1 <- i
         fmt.Println("===> 子go程,写入数据:", i)
      }
   }()


   // 读
   for i := 0; i < 20; i++ {
      // 从管道中读取数据
      data := <- numsChan1
      fmt.Println("主go程 读取数据 ==》 data:", data)
   }

   time.Sleep(5 * time.Second)
}

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

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

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

For-range遍历

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

示例如下:

Go 复制代码
numsChan2 := make(chan int, 20)

//写
go func() {
   for i := 0; i < 15; i++ {
      // 向管道中写入数据
      numsChan2 <- i
      fmt.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)。

相关推荐
waicsdn_haha6 分钟前
Java/JDK下载、安装及环境配置超详细教程【Windows10、macOS和Linux图文详解】
java·运维·服务器·开发语言·windows·后端·jdk
_WndProc8 分钟前
C++ 日志输出
开发语言·c++·算法
Q_192849990617 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
qq_4335545417 分钟前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
良许Linux21 分钟前
0.96寸OLED显示屏详解
linux·服务器·后端·互联网
求知若饥33 分钟前
NestJS 项目实战-权限管理系统开发(六)
后端·node.js·nestjs
数据小爬虫@36 分钟前
如何高效利用Python爬虫按关键字搜索苏宁商品
开发语言·爬虫·python
ZJ_.38 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
Narutolxy44 分钟前
深入探讨 Go 中的高级表单验证与翻译:Gin 与 Validator 的实践之道20241223
开发语言·golang·gin
Hello.Reader1 小时前
全面解析 Golang Gin 框架
开发语言·golang·gin