学习 Go并发模型

简介: 本文通过一个简单例子,讲解如何将数组数据转换为其平方值,并将其分解为三个步骤:生产信息(`producer()`)、处理信息(`square()`)和消费信息(`main()`)。进一步介绍了 FAN-OUT 和 FAN-IN 模型的优化,展示了多 goroutine 并发读写通道的实现方式。FAN-OUT 是多个 goroutine 从同一通道读取数据,而 FAN-IN 是单个 goroutine 从多个通道读取数据。最后强调了优化 FAN 模式时需根据具体场景解决瓶颈问题,并推荐使用带缓冲的通道以提高性能。

1.简单例子

  • 将数组内的数据转变为他们的平方
  • 分解以上过程为三个步骤
  • 生产信息 producer(),遍历切片
  • 处理信息 square(),计算平方
  • 消费信息 main(),消费

1.生产信息

go

代码解读

复制代码

func producer(nums ...int) <-chan int {
	// 创建带缓冲通道
	out := make(chan int,10)
	// 通过协程将数据存储到通道中
	go func(){
		defer close(out) //最后关闭通道
		for _,num := range nums {
			out <- num
		}
	}()
	return out
}

2.处理信息

go

代码解读

复制代码

func square(inCh <-chan int) <-chan int {
	out := make(chan int,10)
	go func(){
		defer cloes(out)
		for n := range inCh {
			out <- n*n
		}
	}()
	return out
}

3.消费信息

go

代码解读

复制代码

func main() {
	// 先将数据拆分放入通道
	in := producer(1,2,3,4)
	// 处理数据
	ch := square(in)
	// 消费数据
	for ret := range ch {
	fmt.Printf("%3d",ret)
	}
}

扇形模型优化 FAN-IN 与 FAN-OUT

  • FAN-OUT : 多个 goruntine 从同一个通道读取数据,直到该通道关闭
  • FAN-IN :1个 goruntine 从多个通道读取数据,直到该通道关闭

1. FAN-OUT 和 FAN-IN 实践

1.生产者producer() 和 消息处理square()不变

go

代码解读

复制代码

func producer(nums ...int) <-chan int {
	// 创建带缓冲通道
	out := make(chan int,10)
	// 通过协程将数据存储到通道中
	go func(){
		defer close(out) //最后关闭通道
		for _,num := range nums {
			out <- num
		}
	}()
	return out
}

func square(inCh <-chan int) <-chan int {
	out := make(chan int,10)
	go func(){
		defer close(out)
		for n := range inCh {
			out <- n*n
		}
	}()
	return out
}

2. 新增merge() 用来多个square() 操作最后回归到一个通道消费读取--- FAN-IN

go

代码解读

复制代码

func merge(cs ...<-chan int) <-chan int {
	out := make(chan int,10)
	
	// 创建计时器
	var wg sync.WaitGroup
	// 将所有数据回归到一个通道中
	collect := func (in <-chan int){
		defer wg.Done()
		for n := range in {
			out <- n
		}
	}
	
	wg.Add(len(cs))
	// FAN - IN
	for _,c := range cs {
		go collect(c)
	}
	
	// 错误方式:直接等待是bug,死锁,因为merge写了out,main却没有读,出现该错误的原因是使用了无缓冲通道,如果要实现这个bug,请将 merge() 中的 make(chan int,10) 改成 make(chan int)
	// wg.Wait()
	 // close(out)
	 
	go func(){
		wg.Wait()
		close(out)
	}()
	
	return out
}

3.修改main(),启动3个square(),一个生产者producer()被多个square()读取 --- FAN-OUT

go

代码解读

复制代码

func main() {
	in := producer(1,2,3,4)
	
	// FAN-OUT  这个时候开启了协程
	c1 := square(in)
	c2 := square(in)
	c3 := square(in)
	
	// consumer
	for ret :=range merge(c1,c2,c3) {
		fmt.Printf("%3d",ret)
	}
}

3.优化 FAN 模式

  • 不同的场景优化不同,要依据具体的情况,解决程序的瓶颈
  • 但总的来说 不推荐用无缓冲通道,推荐用有缓冲通道


转载来源:https://juejin.cn/post/6993297920208584711

相关文章
|
10天前
|
算法 调度 Python
如何在Python中使用Coroutines
本文介绍了在Python中使用轮询程序(coroutines)的方法及其特点。Coroutines是可暂停和恢复执行的函数,与生成器、线程和子程序有所不同。文章详细解释了coroutines的工作原理,包括如何通过`send()`方法传递数据,以及如何用`close()`方法关闭coroutine。此外,还展示了如何构建基于coroutines的流水线结构以处理数据过滤和传输任务。最后总结了coroutines的优势及与其他多任务机制的区别。
|
4月前
|
前端开发 Java 数据库连接
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
119 21
Spring框架初识
|
4月前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
100 3
Go 语言入门指南:切片
|
4月前
|
SQL 存储 关系型数据库
int(1) 和 int(10) 有什么区别?
在MySQL中,`int`类型后面的数字(如`int(1)`、`int(10)`)并不影响其存储范围,最大值仍为4294967295(无符号)。这些数字只有在配合`zerofill`使用时才有意义,用于显示时不足位数补0。例如,`int(4) zerofill`会将1显示为0001。这适用于需要固定长度编号的场景,如学号等。
115 3
int(1) 和 int(10) 有什么区别?
|
5月前
|
Shell Linux Ruby
Python3虚拟环境venv
`venv` 是 Python 的虚拟环境工具,用于为不同项目创建独立的运行环境,避免依赖冲突。通过 `python3 -m venv` 命令创建虚拟环境,并使用 `source bin/activate` 激活。激活后,所有 Python 包将安装在该环境中,不影响系统全局环境。退出环境使用 `deactivate` 命令。每个虚拟环境拥有独立的包集合,确保项目间的隔离性。删除虚拟环境只需删除其目录即可。
175 34
|
4月前
|
Java
java引入本地 MultipartFile 实现多部分文件上传
在Java中,`MultipartFile`通常用于处理通过HTML表单上传的文件。但在某些情况下,需要直接从本地文件系统获取文件并上传。本文介绍如何创建一个实现了`MultipartFile`接口的本地类`LocalMultipartFile`,将本地文件转换为`MultipartFile`对象,简化文件上传流程。此方法适用于批量上传等场景,避免了表单上传的复杂性。代码示例展示了如何实现和使用该类进行文件上传操作。作者:华科云商小彭。链接:[稀土掘金](https://juejin.cn/post/7377559533785530431)。
204 18
|
5月前
|
Java 测试技术 应用服务中间件
Spring Boot 如何测试打包部署
本文介绍了 Spring Boot 项目的开发、调试、打包及投产上线的全流程。主要内容包括: 1. **单元测试**:通过添加 `spring-boot-starter-test` 包,使用 `@RunWith(SpringRunner.class)` 和 `@SpringBootTest` 注解进行测试类开发。 2. **集成测试**:支持热部署,通过添加 `spring-boot-devtools` 实现代码修改后自动重启。 3. **投产上线**:提供两种部署方案,一是打包成 jar 包直接运行,二是打包成 war 包部署到 Tomcat 服务器。
121 10
|
5月前
|
机器学习/深度学习 运维 算法
异常检测的特征工程:提取有价值的信息
本文介绍了异常检测中的特征工程,涵盖背景、核心概念、算法原理及代码实例。异常检测旨在识别数据中的异常行为,广泛应用于金融、医疗等领域。特征工程通过提取、选择和创建特征,提升模型性能。文中详细讲解了统计特征(如均值、方差)、时间序列特征(如移动平均、差分)和域知识特征的提取方法,并提供了Python代码示例。最后讨论了未来的发展趋势与挑战,包括数据复杂性增加、跨领域应用、解释性需求等。
138 1
|
5月前
|
Python
探索Python虚拟环境:virtualenv、venv与pipenv比较
在Python开发中,有效的环境管理至关重要。virtualenv、venv和pipenv是常用的虚拟环境管理工具。virtualenv支持Python 2.7+和3.3+,可创建独立环境;venv为Python 3.3+内置库,简单轻量但功能有限;pipenv则结合了包管理和虚拟环境管理,生成Pipfile.lock确保依赖确定性和安全性,推荐作为首选工具。
225 2
|
9月前
|
Java 应用服务中间件 API
Vertx高并发理论原理以及对比SpringBoot
Vertx 是一个基于 Netty 的响应式工具包,不同于传统框架如 Spring,它的侵入性较小,甚至可在 Spring Boot 中使用。响应式编程(Reactive Programming)基于事件模式,通过事件流触发任务执行,其核心在于事件流 Stream。相比多线程异步,响应式编程能以更少线程完成更多任务,减少内存消耗与上下文切换开销,提高 CPU 利用率。Vertx 适用于高并发系统,如 IM 系统、高性能中间件及需要较少服务器支持大规模 WEB 应用的场景。随着 JDK 21 引入协程,未来 Tomcat 也将优化支持更高并发,降低响应式框架的必要性。
207 6
Vertx高并发理论原理以及对比SpringBoot