深入理解 Go Channel:解密并发编程中的通信机制

一、Channel管道
1、Channel说明
  • 共享内存交互数据弊端
    • 单纯地将函数并发执行是没有意义的。函数与函数间需要交互数据才能体现编发执行函数的意义
    • 虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发送静态问题
    • 为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题
  • channel好处
    • Go语言中的通道(channel)是一种特殊的类型
    • 通道像一个传送带或者队列,总是遵循先入先出(First In First Out)原则,保证收发数据的顺序
    • 每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型
    • 如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接
    • channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制
2、channel类型
  • channel是一种类型,一种引用类型
  • 声明管道类型的格式如下
var 变量 chan 元素类型
var ch1 chan int        //声明一个传递整型的管道
var ch2 chan bool        //声明一个传递布尔型的管道
var che chan []int        //声明一个传递int切片的管道
3、创建channel
  • 声明的管道后需要使用make函数初始化之后才能使用
  • 创建channel的格式如下:make(chan 元素类型,容量)
//创建一个能存储10个int类型数据的管道
ch1 := make(chan int,10)
//创建一个能存储4个bool类型数据的管道
ch2 := make(chan bool,4)
//创建一个能存储3个[]int切片类型数据的管道
ch3 := make(chan []int,3)

二、channel操作

  • 管道有发送(send)、接收(receive)和关闭(close)三种操作
  • 发送和接收使用<-符合
  • 现在我们先使用以下语句定义一个管道
ch := make(chan int,3)
1、发送(将数据放在管道内)
  • 将一个值发送到管道中
ch <- 10    //把10发送到ch中
2、接收(从管道内取值)
  • 从一个管道中接收值
x := <- ch    //从ch中接收值并赋值给变量x
<- ch        //从ch中接收值,忽略结果
3、关闭管道
  • 我们通过调用内置的close函数来关闭管道:close(ch)
  • 关于关闭管道需要注意的事情是,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭管道
  • 管道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭管道不是必须的
  • 关闭后的管道有以下特点:
    • 对一个关闭的管道再发送值就是导致panic
    • 对一个关闭的管道进行接收会一直获取值直到管道为空
    • 对一个关闭的并且没有值的管道执行接收操作会得到对应类型的零值
    • 关闭一个已经关闭的管道会导致panic
4、管道阻塞
1、无缓冲的管道
  • 如果创建管道的时候没有指定容量,那么我们可以叫这个管道为无缓冲的管道
  • 无缓冲的管道有称为阻塞的管道
package main
import "fmt"

func main() {
	ch := make(chan int)
	ch <- 10
	fmt.Println("发送成功")
}
/*
-- 面这段代码能够通过编译,但是执行的时候会出现以下错误
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
	E:/_000/Go/Code/demo1.go:6 +0x5f
 */

2、有缓冲的管道
  • 解决上面问题的方法还有一种就是使用有缓冲区的管道
  • 我们可以在使用make函数初始化管道的时候为其指定管道的容量
  • 只要管道的容量大于零,那么该管道那么该管道就是有缓冲的管道,管道的容量表示管道中能存放元素的数量。
  • 就像你小区的快递柜只有那么个多格子,格子满了就装不下了,就阻塞了,等到别人取走一个快递员就能往里面放一个。
package main
import "fmt"

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

三、从 Channel 取值 

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

import (
	"fmt"
)

func f1(ch1 chan int) {
	for i := 0; i < 100; i++ {
		ch1 <- i
	}
	close(ch1)
}

func f2(ch1 chan int, ch2 chan int) {
	for {
		i, ok := <-ch1 // 通道关闭后再取值ok=false
		if !ok {
			break
		}
		ch2 <- i * i
	}
	close(ch2)
}

// channel 练习
func main() {
	ch1 := make(chan int)
	ch2 := make(chan int)
	// 开启goroutine将0~100的数发送到ch1中
	go f1(ch1)
	// 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
	go f2(ch1, ch2)
	// 在主goroutine中从ch2中接收值打印
	for i := range ch2 { // 通道关闭后会退出for range循环
		fmt.Println(i)
	}
}
2、Goroutine结合Channel管道
  • 需求 1:定义两个方法,一个方法给管道里面写数据,一个给管道里面读取数据,要求同步进行。
  • 1、开启一个 fn1 的的协程给向管道 inChan 中写入 100 条数据
  • 2、开启一个 fn2 的协程读取 inChan 中写入的数据
  • 3、注意:fn1 和 fn2 同时操作一个管道
  • 4、主线程必须等待操作完成后才可以退出
  • 注:for range的方式判断通道关闭,推出程序
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup
func main()  {
	intChan := make(chan int,10)
	wg.Add(2)
	go write(intChan)
	go read(intChan)
	wg.Wait()
	fmt.Println("读取完毕...")
}

func write(intChan chan int)  {
	defer wg.Done()
	for i:=0;i<10;i++{
		intChan <- i
	}
	close(intChan)
}

func read(intChan chan int)  {
	defer wg.Done()
	for v := range intChan {
		fmt.Println(v)
		time.Sleep(time.Second)
	}
}

四、单向管道

  • 有的时候我们会将管道作为参数在多个任务函数间传递
  • 很多时候我们在不同的任务函数中使用管道都会对其进行限制
  • 比如限制管道在函数中只能发送或只能接收
package main
import (
	"fmt"
)
func main() {
	//1. 在默认情况下下,管道是双向
	//var chan1 chan int //可读可写

	//2 声明为只写
	var chan2 chan<- int
	chan2 = make(chan int, 3)
	chan2<- 20
	//num := <-chan2 //error
	fmt.Println("chan2=", chan2)
	
	//3. 声明为只读
	var chan3 <-chan int
	num2 := <-chan3
	//chan3<- 30 //err
	fmt.Println("num2", num2)
}

五、Goroutine池

  • 本质上是生产者消费者模型
  • 在工作中我们通常会使用可以指定启动的goroutine数量–worker pool模式,控制goroutine的数量,防止goroutine泄漏和暴涨。
  • 一个简易的work pool示例代码如下:
package main

import (
	"fmt"
	"time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
	for j := range jobs {
		fmt.Printf("worker:%d start job:%d\n", id, j)
		time.Sleep(time.Second)
		fmt.Printf("worker:%d end job:%d\n", id, j)
		results <- j * 2
	}
}


func main() {
	jobs := make(chan int, 100)
	results := make(chan int, 100)
	// 开启3个goroutine
	for w := 1; w <= 3; w++ {
		go worker(w, jobs, results)
	}
	// 5个任务
	for j := 1; j <= 5; j++ {
		jobs <- j
	}
	close(jobs)
	// 输出结果
	for a := 1; a <= 5; a++ {
		<-results
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTTP、HTTPS、SSL协议以及相关报文讲解

目录 HTTP/HTTPS介绍 HTTP/HTTPS基本信息 HTTP如何实现有状态 HTTP请求与应答报文 HTTP请求报文 HTTP响应报文 SSL协议 SSL单向认证 SSL双向认证 HTTP连接建立与传输步骤 HTTP访问全过程相关报文&#xff08;以访问www.download.cucdccom为例子&#xff09; DNS报文…

什么是SEO优化

什么是SEO&#xff0c;百度其实就有答案&#xff0c;只是回答的很基础&#xff0c;说的都是基础概念&#xff0c;没有具体的体现在里面&#xff0c;SEO除了基础概念&#xff0c;还要有相应的构架&#xff0c;不然怎么弄都是一场空而已。 关于什么是SEO的文章导读&#xff1f; 1…

vite+vue3+electron搭建项目

编辑器使用vscode&#xff0c;打开一个空文件夹 第一步 初始化vite项目 初始化vite项目&#xff0c;命令 npm init vite 第二步 下载依赖 进入新建的项目&#xff0c;下载依赖&#xff0c;命令 cd vite-projec npm i第三步 使用cnpm下载 electron依赖 新建一个终端&#…

springboot应用,cpu高、内存高问题排查

前几天&#xff0c;排查了2个生产问题。一个cpu高&#xff0c;一个内存高。今天把解决过程整理一下 文章目录 1、cpu高问题排查1.1、获取栈日志1.2、分析栈日志 2、内存高问题排查2.1、dump日志分析2.2、堆内存使用情况2.3、解决方案2.4、arthas trace解决问题2.5、总结 1、cp…

【玩转TableAgent数据智能分析】会话式数据分析,所需即所得!

目录 1 TableAgent介绍 2 TableAgent五大优点 3 体验TableAgent 3.1 登录TableAgent平台 3.2 会话式数据分析 4 总结 【优化改善】 【对比TableAgent与文心一言- E言易图】 1 TableAgent介绍 TableAgent是一款数据集成和分析平台&#xff0c;它可以帮助用户从多个数据源中…

Maven的安装配置流程

步骤一&#xff1a;下载Maven 打开Maven官方网站&#xff0c;进入"Download"页面。我这里有下好的&#xff0c;网盘链接在文末&#xff01;&#xff01; 在"Download"页面中找到最新版本的Maven&#xff0c;选择一个稳定的版本。通常&#xff0c;你会看到…

面相对象开发的原则

1、开闭原则 对修改关闭&#xff0c;对扩展打开。 2、里氏替换原则 子类继承父类的方法时&#xff0c;不可重写父类的方法。 如果重写了父类的方法会导致整个继承体系比较差&#xff0c;特别是运用多态比较平凡时&#xff0c;程序运行出错概率较大。 如果出现了违背“里氏替换…

LIN总线信号串行译码

我们用虹科Pico汽车示波器捕捉了LIN总线信号 &#xff0c;如果想看它对应的报文数据&#xff0c;我们可以应用PicoScope Automotive软件的串行译码功能来对它破译。 使用指导如下&#xff1a; 点击“串行译码”&#xff0c;选择对应的协议&#xff0c;如LIN。 在下面对话框&…

04.HTML其他知识

HTML其他知识 1.HTML实体 介绍 在 HTML 中我们可以用一种特殊的形式的内容&#xff0c;来表示某个符号&#xff0c;这种特殊形式的内容&#xff0c;就是 HTML 实体。比如小于号 < 用于定义 HTML 标签的开始。如果我们希望浏览器正确地显示这些字符&#xff0c;我们必须在…

西南科技大学C++程序设计实验十(函数模板与类模板)

一、实验目的 1. 掌握函数模板与类模板; 2. 掌握数组类、链表类等线性群体数据类型定义与使用; 二、实验任务 1. 分析完善以下程序,理解模板类的使用: (1)补充类模板声明语句。 (2)创建不同类型的类对象,使用时明确其数据类型? _template<typename T>__…

Java8新特性:函数式(Functional)接口

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…

php 和 python 跨界 合作 phpy搭建 已解决

目录 需求介绍 安装 windows版本 文件地址 运行效果 需求介绍 在日常功能开发中&#xff0c;难免会使用python的计算库&#xff0c;同时自己要是一名PHP开发工程师。就在最近有相应的需求&#xff0c;索性使用phpy来进行功能开发 安装 windows版本 phpy 是识沃团队最新推出…

@Valid注解的使用

Valid注解的使用 Valid 用来对参数进行验证&#xff0c;在变量信息中添加用于充当校验条件的注解 需要传入BindingResult对象&#xff0c;用于获取校验失败情况下的反馈信息 用法示例

2022年AMC8数学竞赛真题的典型考点和解析

大家好&#xff01; 从战争中学习战争。 在2024年AMC8考前一个多月的时间中&#xff0c;孩子除了完成学校作业外&#xff0c;备考AMC8的有效方法就是做往年真题&#xff0c;通过往年真题进一步了解AMC8的考点、题型&#xff0c;建立自己的解题策略和方法。并且查漏补缺&#…

基于Python数据可视化的网易云音乐歌单分析系统

目录 《Python数据分析初探》项目报告 基于Python数据可视化的网易云音乐歌单分析系统一、项目简介&#xff08;一&#xff09;项目背景&#xff08;二&#xff09;项目过程 二、项目设计流程图&#xff08;一&#xff09;基于Python数据可视化的网易云音乐歌单分析系统的整体…

出海电商访问亚马逊打开很慢!有什么办法可以快速解决?

亚马逊作为全球最大的电商平台&#xff0c;很多中国卖家在平台上做着买卖。亚马逊中国卖每天都要频繁访问亚马逊店铺处理回复邮件和处理订单&#xff0c;上传产品等等事宜&#xff0c;但是时常会遇到打开一个新页面需要等待很长时间&#xff0c;更甚者直接打不开页面! 亚马逊打…

Java实现快速排序及其动图演示

快速排序&#xff08;Quicksort&#xff09;是一种基于分治思想的排序算法。它通过选择一个基准元素&#xff0c;将数组分为两个子数组&#xff0c;其中一个子数组的所有元素都小于基准元素&#xff0c;另一个子数组的所有元素都大于基准元素&#xff0c;然后递归地对这两个子数…

【Linux系统编程】初步运用git工具

介绍&#xff1a; 使用git之前首先要先认识gitee/github&#xff0c;gitee/github是一个远程仓库网站。git是平台专门开发的一个操控工具&#xff0c;是一个开源的分布式版本控制系统&#xff0c;我们使用git工具来与gitee/github来取得联系。 git的推送使用&#xff1a; git既…

六:Day06_Spring Security02

一、访问控制&#xff08;授权&#xff09; 1. 基于资源访问控制 查询用户的权限。 访问资源时判断用户是否具有指定的权限。 1.1 修改UserServiceImpl Service public class UserServiceImpl implements UserDetailsService {Autowiredprivate UserMapper userMapper;Aut…

JS数组与它的42个方法

前言 数组在js中作为一个非常重要的类型之一&#xff0c;在我们对数据处理&#xff0c;存储数据&#xff0c;条件渲染的时候经常会用到&#xff0c;所以随着ES的不断更新&#xff0c;数组的方法也是越来越多&#xff0c;也让我们使用数组对数据操作的时候&#xff0c;越来越简…