当前位置: 首页 > article >正文

Go语言 管道1

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

目录

无缓冲通道

有缓冲的管道

语法

特点

代码示例

未分配空间示例

读取次数不一致示例

For-range遍历

总结


无缓冲通道

sync.RWMutex{}

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

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

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

A 往管道里面写数据 B从管道里面读数据,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)
}

运行结果:

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

运行结果:

===> 主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 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)
}

运行结果:

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

解决方法进行分配空间

示例如下:

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

 

读取次数不一致示例

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

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

示例如下:

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写法。

示例如下:

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


http://www.kler.cn/a/304086.html

相关文章:

  • 《AI 使生活更美好》
  • 期权懂|期权新手入门教学:期权合约有哪些要素?
  • Docker网络和overlay的基础讲解
  • 准确--FastDFS快速单节点部署
  • iOS 18.2 重磅更新:6个大动作
  • Jest项目实战(7):部署文档网站到 GitHub Pages
  • 基于单片机的仔猪喂饲系统设计
  • vue原理分析(八)研究new Vue()中的initProxy
  • 集群聊天服务器项目【C++】(二)Json的简单使用
  • C++笔记---list
  • [QT] QT事件与事件重写
  • Spring扩展点系列-SmartInstantiationAwareBeanPostProcessor
  • 通过策略模式实现对象创建工厂
  • QMainWindow,调用exec()实现QDialog阻塞效果
  • HTTPS和HTTP区别是什么?
  • 【Python机器学习】循环神经网络(RNN)——利用Keras实现循环神经网络
  • 【XR】AR HUD
  • 交换机vlan配置实现
  • Java、python、php三个版本 抗震救灾物资管理系统 抗洪救灾物资分配系统 救援物资申请平台(源码、调试、LW、开题、PPT)
  • Android Framework(四)WMS-窗口显示流程——窗口创建与添加
  • TON的两种地址
  • Linux环境下运行 KF-GINS(GNSS+IMU松组合) 详细步骤
  • CGAL中的网格
  • PL/SQL 继承Oracle Database 的可靠性、安全性和可移植性
  • 初始爬虫1(补充)
  • 某讯/企鹅滑块验证码逆向(一)