golang中的几种并发模式
0.1、索引
https://blog.waterflow.link/articles/1663551951058
1、for- select模式
这种模式通常用在从多个通道读取数据
package main
import (
"fmt"
"time"
)
func main() {
ch1, ch2 := make(chan int), make(chan int)
// 每2秒不断往通道1写数据
go func() {
i := 0
for {
i += 2
ch1 <- i
time.Sleep(2 * time.Second)
}
}()
// 每2秒不断往通道2写数据
go func() {
i := 1
for {
i += 2
ch2 <- i
time.Sleep(2 * time.Second)
}
}()
// 不断从通道读数据
for {
select {
case v := <-ch1:
fmt.Println("ch1:", v)
time.Sleep(time.Second)
case v := <-ch2:
fmt.Println("ch2:", v)
time.Sleep(time.Second)
default:
fmt.Println("default")
time.Sleep(time.Second)
}
}
}
如果ch1和ch2没数据,会走default
如果ch1和ch2都有数据会随机选择一个执行,之所以随机是为了避免只执行第一个case导致饥饿
2、done-channel模式
由于goroutine不会被垃圾回收,因此很可能导致内存泄漏。
为了避免内存泄漏,goroutine应该有被触发取消的机制。父 Goroutine 需要通过一个名为 done 的只读通道向其子 Goroutine 发送取消信号。按照惯例,它被设置为第一个参数。
这种模式在其他模式中也被大量使用。
package main
import (
"fmt"
)
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go doWork(done, jobs)
for j := 1; j <= 3; j++ {
fmt.Println("sent job", j)
jobs <- j
}
close(jobs)
fmt.Println("sent all jobs")
// 任务结束
done <- true
}
func doWork(done chan bool, jobs chan int) {
for {
select {
case j, more := <-jobs:
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
}
case <-done: // 任务结束,关闭子协程
return
default:
}
}
}
3、or-done模式
该模式旨在将多个完成通道组合成一个 agg_done;这意味着如果一个 done 通道发出信号,则整个 agg_done 通道也将关闭。然而,我们不知道在运行时完成通道的数量。
or-done 模式可以通过使用 goroutine 和 递归 来实现。
示例中 使上下递归函数像树一样相互依赖。上部将自己的 orDone 通道注入下部。然后下层也将自己的 orDone 返回给上层。
如果任何 orDone 通道关闭,则通知上层和下层。
这点和上面done-channel模式是不同的,上面是所有goroutine完成任务,这里是只要有1个goroutine完成就结束所有goroutine。
就好比发送一个请求到多个微服务节点,只要有1个返回就算完成。
package main
import (
"fmt"
"time"
)
func main() {
var or func(channels ...<-chan interface{}) <-chan interface{}
// 只要有1个结束阻塞,关闭orDone并返回
or = func(channels ...<-chan interface{}) <-chan interface{} {
// 小于2个通道直接返回
switch len(channels) {
case 0:
return nil
case 1:
return channels[0]
}
// 声明一个orDone
orDone := make(chan interface{})
go func() {
// 完成关闭orDone
defer close(orDone)
switch len(channels) {
case 2: // 如果是2个channel,只需要监听这两个
select {
case <-channels[0]:
case <-channels[1]:
}
default:
// 二分法递归
m := len(channels) / 2
select {
case <-or(channels[:m]...):
case <-or(channels[m:]...):
}
}
}()
return orDone
}
// 传入一个时间模拟请求时长,时间到了就close掉,结束当前channel的阻塞
sig := func(after time.Duration) <-chan interface{} {
c := make(chan interface{})
go func() {
defer close(c)
time.Sleep(after)
}()
return c
}
start := time.Now()
// 这里orDone开始是阻塞的,里面开了5个channel
<-or(
sig(2*time.Hour),
sig(5*time.Minute),
sig(1*time.Second),
)
fmt.Printf("done after %v\n", time.Since(start))
}
4、fanout-channel模式
意思是只有1个输入channel,有多个输出channel,经常用在设计模式中的观察者模式。观察者模式中,当数据发生变动后,多个观察者都会收到这个信号。
package main
import (
"fmt"
"time"
)
func main() {
// 输入的channel,相当于被观察者
ch := make(chan interface{})
go func() {
for {
ch <- time.Now()
time.Sleep(3 * time.Second)
}
}()
// 观察者
out := make([]chan interface{}, 2)
for k := range out {
out[k] = make(chan interface{})
}
go fanout(ch, out)
// 是否观察到数据变化
for {
select {
case res := <-out[0]:
fmt.Println(res)
case res := <-out[1]:
fmt.Println(res)
}
}
}
func fanout(ch <-chan interface{}, out []chan interface{}) {
defer func() {
for i := 0; i < len(out); i++ {
close(out[i])
}
}()
// 订阅被观察者
for v := range ch {
v := v
for i := 0; i < len(out); i++ {
i := i
out[i] <- v
}
}
}
5、fan-in-channel模式
和上面的相反,这个是指多个源channel输入,一个目标channel输出的情况。
package main
import (
"fmt"
"time"
)
func main() {
// 输入的channel
in := make([]chan interface{}, 2)
in2 := make([]<-chan interface{}, 2)
for k := range in {
k := k
in[k] = make(chan interface{})
var inin <-chan interface{} = in[k]
in2[k] = inin
go func() {
for {
in[k] <- time.Now()
time.Sleep(3 * time.Second)
}
}()
}
// 打印输出的channel
for v := range fanIn(in2...) {
fmt.Println(v)
}
}
func fanIn(chans ...<-chan interface{}) <-chan interface{} {
switch len(chans) {
case 0:
c := make(chan interface{})
close(c)
return c
case 1:
return chans[0]
case 2:
return mergeTwo(chans[0], chans[1])
default: // 多个channel二分法
m := len(chans) / 2
return mergeTwo(fanIn(chans[:m]...), fanIn(chans[m:]...))
}
}
func mergeTwo(a, b <-chan interface{}) <-chan interface{} {
// 针对2个channel输出
c := make(chan interface{})
go func() {
defer close(c)
for a != nil || b != nil {
select {
case v, ok := <-a:
if !ok {
a = nil
continue
}
c <- v
case v, ok := <-b:
if !ok {
b = nil
continue
}
c <- v
}
}
}()
return c
}
golang中的几种并发模式的更多相关文章
- python万能消费框架,新增7种中间件(或操作mq的包)和三种并发模式。
新增的中间件和并发模式见注释. 消息队列中间件方面celery支持的,都要支持.并发模式,celery支持的都要支持. 从无限重复相似代码抽取框架,做成万能复用,是生产力的保障. 使用模板模式使加新中 ...
- go--->共享内存和通信两种并发模式原理探究
共享内存和通信两种并发模式原理探究 并发理解 人类发明计算机编程的本质目的是为了什么呢?毫无疑问是为了解决人类社会中的各种负责业务场景问题.ok,有了这个出发点,那么想象一下,比如你既可以一心一意只做 ...
- Activity中的四种启动模式
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...
- VMWare中的三种联网模式图解
网络基础及局域网配置 1.简单的局域网结构 2.VMWare中的三种联网模式 NAT模式 桥接模式 VMnet1
- Http中的三种请求处理模式(MPM)的区别
MPM---包括基于事件/异步,线程化和预分叉 MPM(multi-processing module)多种请求处理模式,分为三种工作模式: prefork worker event prefork- ...
- 详解 Java 中的三种代理模式
代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...
- [Swift实际操作]八、实用进阶-(2)Swift语言中的三种消息传递模式
本文将通过响应按钮的点击事件,来演示Target-Action消息传递机制,该机制主要用于响应用户的界面操作.打开创建的空白项目.然后在左侧的项目导航区,打开视图控制器的代码文件:ViewContro ...
- this与JavaScrip中的四种调用模式
this是什么 方法调用模式 构造器调用模式 函数调用模式 apply/call模式 this是什么 —In most languages, ‘this’ is a reference to the ...
- P2P网贷中的4种理财业务模式
线上3种 直投标:线上理财人直接购买借款人的标,平台只是起个"撮合"作用,收点借款人的服务费. 借款人不还钱,有的平台会帮"借款人"还 ...
随机推荐
- 使用.NET简单实现一个Redis的高性能克隆版(六)
译者注 该原文是Ayende Rahien大佬业余自己在使用C# 和 .NET构建一个简单.高性能兼容Redis协议的数据库的经历. 首先这个"Redis"是非常简单的实现,但是他 ...
- Luogu2375 [NOI2014]动物园 (KMP)
写炸,上网,不同KMP形态. 无力,照该,一换写法就过. 横批:我是垃圾 求\(next\)时\(DP\)出\(num\),路径压缩防卡\(n^2\) AC #include <iostream ...
- Luogu3267 [JLOI2016/SHOI2016]侦察守卫 (树形DP)
树形DP,一脸蒙蔽.看了题解才发现它转移状态与方程真不愧神题! \(f[x][y]\)表示\(x\)的\(y\)层以下的所有点都已经覆盖完,还需要覆盖上面的\(y\)层的最小代价. \(g[x][y] ...
- 七分钟学会 HTML 网页制作
什么是HTML 点击打开视频讲解更加详细 Hyper Text Markup Language(超文本标记语言) 标签控制排版 体积小,方便传输 编写HTLML 推荐使用:VS Code <!D ...
- mybatisplus使用xml
一.配置xml路径 mybatis-plus: mapper-locations: classpath:mapper/*.xml 二.编写Mapper里面的方法 public interface Us ...
- Spring源码环境搭建
Spring源码在github上,地址是https://github.com/spring-projects/spring-framework/,选择5.3.x版本,直接从github上克隆项目网速很 ...
- RabbitMQ协议-AMQP 0-9-1 (高级消息队列协议)
工作模型 producer:生产者 Connection:TCP长连接,AMQP 0-9-1 连接通常是长期存在的.AMQP 0-9-1 是一个应用层协议,它使用 TCP 进行可靠传输.连接使用身份验 ...
- 强大多云混合多K8S集群管理平台Rancher入门实战
@ 目录 概述 定义 为何使用 其他产品 安装 简述 规划 基础环境 Docker安装 Rancher安装 创建用户 创建集群 添加Node节点 配置kubectl 创建项目和名称空间 发布应用 偏好 ...
- 使用.Net对图片进行裁剪、缩放、与加水印
图片的裁剪.缩放.与加水印,是任何系统经常要用到的功能,它们现已集成到IUtility工具中,使用十分简便.(具体代码将在文末给出,支持.NET/.NET Framework/.NET Core) 现 ...
- Eclipse配置Tomcat搭建java Web (JSP)开发环境
配置Tomcat服务 1.打开窗口-首选项-Server-Runtiome Environments 2.点击ADD,选择对应的Tomcat版本,点击下一步 路径选择Tomcat解压后的文件夹目录,点 ...