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中的几种并发模式的更多相关文章

  1. python万能消费框架,新增7种中间件(或操作mq的包)和三种并发模式。

    新增的中间件和并发模式见注释. 消息队列中间件方面celery支持的,都要支持.并发模式,celery支持的都要支持. 从无限重复相似代码抽取框架,做成万能复用,是生产力的保障. 使用模板模式使加新中 ...

  2. go--->共享内存和通信两种并发模式原理探究

    共享内存和通信两种并发模式原理探究 并发理解 人类发明计算机编程的本质目的是为了什么呢?毫无疑问是为了解决人类社会中的各种负责业务场景问题.ok,有了这个出发点,那么想象一下,比如你既可以一心一意只做 ...

  3. Activity中的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  4. VMWare中的三种联网模式图解

    网络基础及局域网配置 1.简单的局域网结构 2.VMWare中的三种联网模式 NAT模式 桥接模式 VMnet1

  5. Http中的三种请求处理模式(MPM)的区别

    MPM---包括基于事件/异步,线程化和预分叉 MPM(multi-processing module)多种请求处理模式,分为三种工作模式: prefork worker event prefork- ...

  6. 详解 Java 中的三种代理模式

    代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能. 这里使用 ...

  7. [Swift实际操作]八、实用进阶-(2)Swift语言中的三种消息传递模式

    本文将通过响应按钮的点击事件,来演示Target-Action消息传递机制,该机制主要用于响应用户的界面操作.打开创建的空白项目.然后在左侧的项目导航区,打开视图控制器的代码文件:ViewContro ...

  8. this与JavaScrip中的四种调用模式

    this是什么 方法调用模式 构造器调用模式 函数调用模式 apply/call模式 this是什么 —In most languages, ‘this’ is a reference to the ...

  9. P2P网贷中的4种理财业务模式

     线上3种   直投标:线上理财人直接购买借款人的标,平台只是起个"撮合"作用,收点借款人的服务费.           借款人不还钱,有的平台会帮"借款人"还 ...

随机推荐

  1. 万答#18,MySQL8.0 如何快速回收膨胀的UNDO表空间

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 背 ...

  2. 推荐几款最好用的MySQL开源客户端,建议收藏!

    一.摘要 众所周知,MYSQL 是目前使得最广泛.最流行的数据库技术之一,为了更方便的管理数据库,市场上出现了大量软件公司和个人开发者研发的客户端工具,比如我们所熟知的比较知名的客户端: Navica ...

  3. 花一分钟体验 Apache DolphinScheduler 第一个官方 Docker 镜像

    先前Apache DolphinScheduler 社区一直是发布 Dockerfile 和 K8s Chart.yaml 文件,由用户自行 build 镜像.随着越来越多的用户伙伴们的呼声高涨,社区 ...

  4. PerfView专题 (第五篇):如何寻找 C# 托管内存泄漏

    一:背景 前几篇我们聊的都是 非托管内存泄漏,这一篇我们再看下如何用 PerfView 来排查 托管内存泄漏 ,其实 托管内存泄漏 比较好排查,尤其是用 WinDbg,毕竟C#是带有丰富的元数据,不像 ...

  5. Web 前端实战:Gitee 贡献图

    前言 这次要做的 Web 前端实战是一个 Gitee 个人主页下的贡献图(在线 Demo),偶尔做一两个,熟悉熟悉 JS 以及 jQ.整体来说这个案例并不难,主要是控制第一个节点以及最后一个节点处于星 ...

  6. iommu分析之---smmu v3的实现

    smmu 除了完成 iommu 的统一的ops 之外,有自己独特的一些地方. 1.Stream Table Stream Table是存在内存中的一张表,在SMMU设备初始化的时候由驱动程序创建好. ...

  7. 【NOI P模拟赛】校门外歪脖树上的鸽子(树链剖分)

    题面 2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ m ≤ 2 × 1 0 5 , 1 ≤ l ≤ r ≤ n , 1 ≤ d ≤ 1 0 8 2 ≤ n ≤ 2 × 10^5,1 ≤ m ≤ 2 ...

  8. Oracle 与 PostgreSQL 函数行为的差异引发性能差异

    对于Oracle,对于数据修改的操作通过存储过程处理,而对于函数一般不进行数据修改操作.同时,函数可以通过 Select 进行调用,而存储过程则不行. 一.对于volatile 函数的行为 1.Ora ...

  9. 监控linux多个cpu的负载情况

    监控linux多个cpu的负载情况 top然后按数字键1

  10. 二进制redis集群部署

    二进制redis集群部署 〇.前言 无聊想学罢了 准备环境: 三台centos7 1C1GB即可 三个路相连的地址 主机 IP 节点-角色-实例(端口) redis1 172.16.106.128 M ...