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

Go并发(2)

2、Go并发

Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信

如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制。

Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

2.1channel类型

channel类型是一种引用类型,声明通道类型的格式如下:

var 变量 chan 元素类型

//例子如下
var ch1 chan int //声明一个传递整形的通道
var ch2 chan bool
var ch3 chan [] int


2.2创建channel

通道是一种引用类型,通道的类型的默认是nil

var ch chan int
fmt.Println(ch)//<nil>

//声明的通道后需要使用`make`函数初始化之后才能使用。

//创建channel的格式如下:

make(chan 元素类型, [缓冲大小])

channel的缓冲大小是可选的。

举几个例子:

ch4:=make(chan int)
ch5:=make(chan bool)
ch6:=make(chan []int)

2.3channel操作

通道有发送(send)、接收(receive)和关闭(close)三种操作

发送和接收都是使用**<-** 符号

定义一个通道如下

ch:=make(chan int)
2.3.1发送

将一个值发送给通道中。

ch<-100 //将100发送给创建ch中
2.3.2接收

从一个通道中来接收值

x:=<-ch //就是从ch中将接受值并赋值给x变量
<-ch   //从其中取出,但忽略结果
2.3.3关闭

通过内置的close来进行关闭通道的操作

close(ch)

关于关闭通道注意:,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。

关闭后的通道的特点如下:

1.对其再发送值就会导致panic

2.如果对其进行接收会一直获取值直到通道为空

3.如果对一个已经关闭的并且没有值的通道执行接收操作会获得对应类型的零值

4.关闭一个已经关闭的通道会导致panic

通道又分为有缓冲和无缓冲两者之分

2.4无缓冲的通道

​ 无缓冲的通道又称为阻塞的通道,代码如下

func main(){
	ch:=make(chan int)
	ch<-10
	fmt.Println("发送成功!")
}

代码成功通过编译,但是执行时会出现以下错误:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        .../src/github.com/Q1mi/studygo/day06/channel02/main.go:8 +0x54

那么为什么会出现deadlock报错呢?

因为:使用ch := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送

上面的代码会阻塞在ch <- 10这一行代码形成死锁,那如何解决这个问题呢?

方法如下:一种方法是启用一个goroutine去接收值,例如:

func recv(c chan int){
	ret:=<-c
	fmt.Println("接收成功!",ret)
}
func main(){
	ch:=make(chan int)
	go recv(ch)   //启用gorountine从通道中接受值
	ch<-10
	fmt.Println("发送成功!")
}

无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。

使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道

2.5有缓冲的通道

​ 解决上面问题的方法还有一种就是使用有缓冲区的通道。我们可以在使用make函数初始化通道的时候为其指定通道的容量,例如:

func main() {
	ch := make(chan int, 1) // 创建一个容量为1的有缓冲区通道
	ch <- 10
	fmt.Println(&quot;发送成功&quot;)
}

一般来说:只要通道的容量大于0,那么就是有缓冲的通道,通道的容量表示通道中能存放的元素的数量,就像小区的快递柜,只有有限的格子,当格子放满了,装不下,快递柜就发生了阻塞,等待,只有等到别人取走一个快递员才能继续往里面放一个。

使用内置的len函数进行获取通道内元素的数量,使用cap函数获取通道的容量。但一般不这样做。

2.6如何进行通道循环取值操作

当通过通道发送有限的数据时,我们可以通过close函数关闭通道来告知该通道接收值的goroutine停止等待。当通道被关闭时,往该通道发送值会引发panic,从该通道里接收的值一直都是类型零值。那如何判断一个通道是否被关闭了呢?

例子如下:

package main

import "fmt"

func main() {
   //创建管道
   ch1 := make(chan int)
   ch2 := make(chan int)
   //开启goroutine将0~100的数发送到ch1中
   go func() {
      for i := 0; i < 100; i++ {
         ch1 <- i
      }
      close(ch1)
   }()
   //开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
   go func() {
      for {
         i, ok := <-ch1 // 通道关闭后再取值ok=false
         if !ok {
            break
         }
         ch2 <- i * i
      }
      close(ch2)
   }()
   //再从主goroutine中从ch2中接收值打印
   for i := range ch2 { // 通道关闭后会退出for range循环
      fmt.Println(i)
   }
}

从上面可知,有两种方式在接受值得时候进行判断通道是否被关闭,通常使用for range得方式


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

相关文章:

  • WorkFlow源码剖析——Communicator之TCPServer(下)
  • old-cms(原生PHP开发的企业网站管理系统)
  • 力扣--树题总结
  • 组件间通信(组件间传递数据)
  • iOS 18.2 重磅更新:6个大动作
  • 深入浅出rust内存对齐
  • JUC-01 线程的创建和状态转换
  • 浪潮:2022年净利同比增长51.39%
  • Windows服务器使用代码SSH免密登录并执行脚本
  • ChatGPT自动化提高工作效率: 2分钟快速生成思维导图
  • 中科图新BIM+GIS数字化设计施工管理解决方案
  • c++学习之类与对象2
  • Nest教程一(准备阶段)
  • JS面试题之ajax、axios、fetch的区别
  • React 搜索时遇到的坑
  • 适配器模式:C++设计模式中的瑞士军刀
  • 确保实时操作系统(RTOS)设备中的数据安全
  • RHCE第一次作业at和cront两个任务管理程序的区别
  • [python刷题模板] 博弈入门-记忆化搜索/dp/打表
  • 基于逻辑回归构建肿瘤预测模型
  • Java面试题总结 | Java基础部分2(持续更新)
  • 《人体地图》笔记
  • 【京准小课堂】NTP网络校时服务器(时间同步系统)参数详解
  • 企业级信息系统开发讲课笔记2.3 利用MyBatis实现关联查询
  • windows下的wsl2如何进行docker数据卷挂载
  • 1.1 Docker Engine-详细介绍