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)。

相关推荐
陌小呆^O^1 分钟前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
ifanatic4 分钟前
[面试]-golang基础面试题总结
面试·职场和发展·golang
懒是一种态度12 分钟前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
I_Am_Me_17 分钟前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
重生之我是数学王子27 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手29 分钟前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z33 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
神仙别闹41 分钟前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
XINGTECODE42 分钟前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
入 梦皆星河44 分钟前
在 Ubuntu/Debian 上安装 Go
ubuntu·golang·debian