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. Floyd算法详解

    Floyd本质上使用了DP思想,我们定义\(d[k][x][y]\)为允许经过前k个节点时,节点x与节点y之间的最短路径长度,显然初始值应该为\(d[k][x][y] = +\infin (k, x, ...

  2. 不止跑路,拯救误操作rm -rf /*的小伙儿

    摘要:误执行了 rm -rf /* 之后,除了跑路还能怎么办? 本文分享自华为云社区<拯救被 rm -rf 伤到的小伙>,作者:Gauss 松鼠会. 灵魂画师再次上线   在开饭前我们先了 ...

  3. 倒计时0日!Apache DolphineScheduler4月 Meetup 大佬手把手教你大数据开发,离线调度

    随着互联网技术和信息技术的发展,信息的数据化产生了许多无法用常规工具量化.处理和捕捉的数字信息.面对多元的数据类型,海量的信息价值,如何有效地对大数据进行挖掘分析,对大数据工作流进行调度,是保障企业大 ...

  4. LuoguP1516 青蛙的约会 (Exgcd)

    #include <cstdio> #include <iostream> #include <cstring> #include <algorithm> ...

  5. Taurus.MVC 微服务框架 入门开发教程:项目部署:4、微服务应用程序发布到Docker部署(上)。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 开源地址:https://github.com/cyq1162/Taurus.MVC 本系列第一篇:Tauru ...

  6. 新年趣事之红包--"四边形"不等式优化DP

    目录 题目描述 输入 输出 思路 新年趣事之红包 时间限制: 1 Sec  内存限制: 64 MB 题目描述 xiaomengxian一进门,发现外公.外婆.叔叔.阿姨--都坐在客厅里等着他呢.经过仔 ...

  7. 【java】学习路线8-cmd带命令编译包

    /*java类包(package)package XX.XX.XX;    包名命名规则:(以域名开头,都是小写)        com.remooo.xx        编译:javac -d . ...

  8. 【python】一些python用法规律笔记

    作为本科用了多年MATLAB的工科生,学起来python有些似曾相识但也有些不习惯的地方. 在这里总结一下,慢慢整理,希望能巩固python语法 一.前闭后开 这个是和MATLAB很大不同.不论是ra ...

  9. 创建一个k8s私有仓库-harbor

    〇.前言 这一步应该是在搭建k8s之前做好,注意了奥 一.安装docker和docker-compose 1.下载docker-compose的最新版本 # 建议那种网上冲浪下载!,下载下来记得命名成 ...

  10. The 19th Zhejiang Provincial Collegiate Programming Contest

    目录 A.JB Loves Math B.JB Loves Comma C. JB Wants to Earn Big Money G. Easy Glide I. Barbecue L. Candy ...