GO学习之 通道(Channel)

GO系列

1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)

文章目录

  • GO系列
  • 前言
  • 一、Channel 简介
  • 二、初始化通道
    • 2.1 无缓冲通道
    • 2.2 有缓冲通道
    • 2.3 单向通道
  • 三、通道操作
    • 3.1 关闭通道
  • 四、通道异常情况
  • 五、总结

前言

按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
现在的互联网项目中,并发量是衡量一个项目的相当重要的指标,如果一个项目不能支持高并发,就好像一辆超跑用了一缸发动机,中看不中用,一点意义都没有啊。
那怎么支持高并发呢,首当其冲的肯定是多线程了,那多线程就势必会带来一个问题,数据的同步和安全!
当然此篇不说多线程,先说说支持多线程之间数据同步的 Channel。
虽然可以用共享内存进行数据交互,但是共享内存在不同的 goroutine 中很容易发生资源竞争的问题,所以为了保证数据交互的正确性,必须使用锁对内存进行加锁,但是这种做法就会带来性能问题了。
Go 语言的并发模型是 CSP(Communicating Sequential Processes),提倡通过通信共享内存实现线程间的数据交互。

一、Channel 简介

Go 语言中的通道(Channel)是一种特殊的类型。见名知意,一边进来另一边出去,或者说通道就像一个队列,总是遵循 先入后出(First In Frist Out) 的规则,以便能保证顺序。
每个通道都是一个具体类型的管道,也就是在声明的时候需要指定元素的数据类型一样。

二、初始化通道

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

var 变量名 chan 元素类型

package mainimport "fmt"func main() {// 声明一个空的channelvar ch1 chan intfmt.Printf("初始化ch1: %+v\n", ch1)ch1 <- 1ch1 <- 2ch1 <- 3fmt.Printf("%+v\n", ch1)
}

运行结果:

PS D:\workspaceGo> go run channelTest.go
初始化ch1: <nil>
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send (nil chan)]:
main.main()D:/workspaceGo/channelTest.go:9 +0x73
exit status 2

在声明后的 ch1 接受元素是报错了,因为我们声明的 ch1 其实是一个 nil,向一个空的channel发送元素则会报错。
声明的通道需要使用 make() 函数初始化之后才能使用。

2.1 无缓冲通道

package mainimport "fmt"func main() {// 声明并且初始化channelch1 := make(chan int)fmt.Printf("初始化ch1: %+v\n", ch1)ch1 <- 1
}

运行结果:

PS D:\workspaceGo> go run channelTest.go
初始化ch1: 0xc0000220c0
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
main.main()D:/workspaceGo/channelTest.go:9 +0x85
exit status 2

这里为什么会出现 deadlock 错误呢?
答:因为我们创建的是无缓冲通道,无缓冲的通道只有在有其他地方接受值的情况下才能发送值。上面的示例中阻塞在 ch1 <- 1 形成死锁,如何解决呢?我们可以用另一个 goroutine 去接受值,如下:

package mainimport "fmt"func main() {// 声明并且初始化channelch1 := make(chan int)// 启动一个 gorountine从通道接收值go receive(ch1)ch1 <- 1ch1 <- 2ch1 <- 3fmt.Println("发送完成!")
}func receive(c chan int) {for i := 0; i < 5; i++ {r := <-cfmt.Printf("接收到的值为:%+v\n", r)}
}

运行结果:

PS D:\workspaceGo> go run channelTest.go
接收到的值为:1
接收到的值为:2
接收到的值为:3
发送完成!

在这个示例中,单独启动一个 goruntine来循环从 ch1 通道中获取值,这样就可以通过 ch1 <- * 不断的往里面放值了。

2.2 有缓冲通道

可以在 make() 函数初始化通道的时候为其指定通道的容量,如下:

package mainimport ("fmt""time"
)func main() {// 声明并且初始化channelch1 := make(chan int, 5)go receive(ch1)for i := 0; i < 10; i++ {fmt.Printf("向ch1中发送 %d 个元素 \n", i)ch1 <- i}fmt.Println("发送完成!")ch1 <- 6
}func receive(c chan int) {for i := 0; i < 10; i++ {time.Sleep(time.Second * 3)r := <-cfmt.Printf("接收到的值为:%+v\n", r)}
}

运行结果:

PS D:\workspaceGo> go run channelTest.go
向ch1中发送 0 个元素 
向ch1中发送 1 个元素
向ch1中发送 2 个元素
向ch1中发送 3 个元素
向ch1中发送 4 个元素
向ch1中发送 5 个元素
接收到的值为:0
向ch1中发送 6 个元素
接收到的值为:1
向ch1中发送 7 个元素
接收到的值为:2
向ch1中发送 8 个元素
接收到的值为:3
向ch1中发送 9 个元素
接收到的值为:4
发送完成!

初始化通道是制定缓存数量,可以往通道中放入初始化数量以内个数的元素,但是超过了初始化量则会阻塞,等通道中空闲出来

2.3 单向通道

三、通道操作

通道有三种操作:

  • 发送:上面例子中已包含
  • 接受:上面例子中已包含
  • 关闭

3.1 关闭通道

可以通过 close() 函数来关闭 channel(对通道的发送和接受完毕,记得关闭通道),如下:

package main
import ("fmt"
)
func main() {// 声明并且初始化channelch1 := make(chan int, 1)go receive(ch1)for i := 0; i < 5; i++ {fmt.Printf("向ch1中发送 %d 个元素 \n", i)ch1 <- i}fmt.Println("发送完成!")// 关闭通道close(ch1)
}
func receive(c chan int) {for i := 0; i < 10; i++ {r, ok := <-cif !ok {fmt.Printf("接收到的值为:%+v\n", r)} else {fmt.Println("通道已关闭!")}}
}

运行结果:

PS D:\workspaceGo> go run channelTest.go
向ch1中发送 0 个元素 
向ch1中发送 1 个元素
通道已关闭!
向ch1中发送 2 个元素
通道已关闭!
向ch1中发送 3 个元素
通道已关闭!
向ch1中发送 4 个元素
通道已关闭!
发送完成!

初始化通道,缓冲为 1 ,当向 ch1 通道中发送元素后,就立马关闭 通道,在 receive() 函数中只能接受到部分元素。

四、通道异常情况

操作nil非空没满
接受deadlock接受成功阻塞接受成功接受成功
发送deadlock发送成功发送成功阻塞发送成功
关闭panic关闭成功,关闭后,接受到 0 值关闭成功,接收到 0 值关闭成功,接受到0值关闭成功,接受到0值

五、总结

通道(Channel)基本的初始化和发送、接受、关闭等操作基本在此篇中体现,使用起来还是间接明了。不像 Java 还需要利用一下锁去控制线程之间的安全问题,虽说是对多线程有较好的支持,但是线程间的数据共享确实实现比较复杂,运用也不简单,需要去深挖。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过,如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/13511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Java] 单例设计模式详解

模式定义&#xff1a;保证一个类只有一个实例&#xff0c;并且提供一个全局访问点&#xff0c;时一种创建型模式 使用场景&#xff1a;重量级的对象&#xff0c;不需要多个实例&#xff0c;如线程池&#xff0c;数据库连接池 单例设计模式的实现 1.懒汉模式&#xff1a;延迟…

第一次后端复习整理(JVM、Redis、反射)

1. JVM 文章仅为自身笔记 详情查看一篇文章掌握整个JVM&#xff0c;JVM超详细解析&#xff01;&#xff01;&#xff01; 1.1 什么是JVM jvm是Java虚拟机 1.2 Java文件的编译过程 程序员编写代码形成.java文件经过javac编译成.class文件再通过JVM的类加载器进入运行时数据…

【141. 环形链表】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#x…

JVM | 基于类加载的一次完全实践

引言 我在上篇文章&#xff1a;JVM | 类加载是怎么工作的 中为你介绍了Java的类加载器及其工作原理。我们简单回顾下&#xff1a;我用一个易于理解的类比带你逐步理解了类加载的流程和主要角色&#xff1a;引导类加载器&#xff0c;扩展类加载器和应用类加载器。并带你深入了解…

剑指offer12 矩阵中的路径 13 机器人的运动范围 34.二叉树中和为某一值得路径

class Solution { public:bool exist(vector<vector<char>>& board, string word) {int rowboard.size(),colboard[0].size();int index0,i0,j0;if(word.size()>row*col) return 0;//vector<vector<int>> visit[row][col];//标记当前位置有没有…

算法之归并排序算法

归并&#xff08;Merge&#xff09;排序法是将两个&#xff08;或两个以上&#xff09;有序表合并成一个新的有序表&#xff0c;即把待排序序列 分为若干个子序列&#xff0c;每个子序列是有序的。然后再把有序子序列合并为整体有序序列。 public class MergeSortTest {public …

到底什么是前后端分离

目录 Web 应用的开发主要有两种模式&#xff1a; 前后端不分离 前后端分离 总结 Web 应用的开发主要有两种模式&#xff1a; 前后端不分离 前后端分离 理解它们的区别有助于我们进行对应产品的测试工作。 前后端不分离 在早期&#xff0c;Web 应用开发主要采用前后端不…

linux系统下(centos7.9)安装Jenkins全流程

一、卸载历史版本 # rpm卸载 rpm -e jenkins# 检查是否卸载成功 rpm -ql jenkins# 彻底删除残留文件 find / -iname jenkins | xargs -n 1000 rm -rf二、环境依赖安装 yum -y install epel-releaseyum -y install daemonize三、安装Jenkins Jenkins官网传送带&#xff1a; …

《零基础入门学习Python》第071讲:GUI的终极选择:Tkinter8

虽然我们能用 tkinter 设计不少东西了&#xff0c;但是不少同学还是感觉对这个界面编程掌控得还不够多&#xff0c;说白了&#xff0c;就是我们现在还没办法随心所欲的去绘制我们想要的界面&#xff0c;但是不瞒你说&#xff0c;今天的这一节课将会给你的人生乃至人生观带来翻天…

VB+access文档管理系统设计与实现

内容摘要 《文档管理系统》是采用VISAUL BASIC6.0开发的一个数据库管理系统。本设计说明书主要讲述了VISAUL BASIC6.0的基本功能及设计方法。紧接着以本系统为例,逐一介绍开发本系统系统的步骤:系统分析、系统设计、系统实现、系统维护。在系统分析中先后用数据流图、数据字…

Android程序CPU使用大的异常分析

程序出现CPU使用过高的问题&#xff0c;如果能够重现&#xff0c;就比较好办了&#xff0c;可以top命令查看各线程的cpu使用&#xff0c;定位到线程。 以下是问国内某AI的答案 在Android应用中&#xff0c;如果某个应用消耗了大量的CPU资源&#xff0c;可以采取以下方法进行分…

分布式id、系统id、业务id以及主键之间的关系

推荐 连分布式ID都理解不了&#xff0c;你是刚培训出来冒充面试官的吧 1 分布式id、系统id、业务id以及主键之间的关系 分布式ID、系统ID、业务ID和主键的关系&#xff1a; 分布式ID&#xff1a;在分布式系统中&#xff0c;由于存在多个独立的节点&#xff0c;为了保证每个节…

苍穹外卖-day07

苍穹外卖-day07 本项目学自黑马程序员的《苍穹外卖》项目&#xff0c;是瑞吉外卖的Plus版本 功能更多&#xff0c;更加丰富。 结合资料&#xff0c;和自己对学习过程中的一些看法和问题解决情况上传课件笔记 视频&#xff1a;https://www.bilibili.com/video/BV1TP411v7v6/?sp…

CSS盒子模型(HTML元素布局)

CSS盒子模型是一种用于描述HTML元素布局的模型&#xff0c;它将每个元素看作是一个矩形的盒子&#xff0c;每个盒子由内容、内边距、边框和外边距组成。 盒子模型包括以下几个部分&#xff1a; 内容区域&#xff08;Content&#xff09; 内容区域是盒子中实际显示内容的部分&am…

Rust vs Go:常用语法对比(五)

题图来自 Rust vs Go 2023[1] 81. Round floating point number to integer Declare integer y and initialize it with the rounded value of floating point number x . Ties (when the fractional part of x is exactly .5) must be rounded up (to positive infinity). 按规…

KWP2000协议和OBD-K线

KWP2000最初是基于K线的诊断协议&#xff0c; 但是由于后来无法满足越来越复杂的需求&#xff0c;以及自身的局限性&#xff0c;厂商又将这套应用层协议移植到CAN上面&#xff0c;所以有KWP2000-K和KWP2000-CAN两个版本。 这篇文章主要讲基于K线的早期版本协议&#xff0c;认…

零售企业信息化系统建设与应用解决方案

导读&#xff1a;原文《零售企业信息化系统建设与应用解决方案ppt》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 完整版领取方式 如需获取完整的电子版内容参考学习…

基于Kaggle训练集预测的多层人工神经网络的能源消耗的时间序列预测(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f308;3 Matlab代码实现 &#x1f389;4 参考文献 &#x1f4a5;1 概述 本文为能源消耗的时间序列预测&#xff0c;在Matlab中实现。该预测采用多层人工神经网络&#xff0c;基于Kaggle训练集预测未来能源消耗。 &am…

使用镜像搭建nacos集群

安装并配置 docker 1 先安装docker //1.查看操作系统的发行版号 uname -r//2.安装依赖软件包 yum install -y yum-utils device-mapper-persistent-data lvm2//3.设置yum镜像源 //官方源&#xff08;慢&#xff09; yum-config-manager --add-repo http://download.docker.co…

第十二章:priority_queue类

系列文章目录 文章目录 系列文章目录前言priority_queue的介绍priority_queue的使用容器适配器什么是容器适配器STL标准库中stack和queue的底层结构 总结 前言 priority_queue是容器适配器&#xff0c;底层封装了STL容器。 priority_queue的介绍 priority_queue文档介绍 优先…