Go并发可视化解释 - Select语句

news2024/11/16 13:43:24

昨天,我发布了一篇文章,用可视化的方式解释了Golang中通道(Channel)的工作原理。如果你对通道的理解仍然存在困难,最好呢请在阅读本文之前先查看那篇文章。作为一个快速的复习:Partier、Candier 和 Stringer 经营着一家咖啡店。Partier 协助从顾客接收订单,然后将这些订单传递给厨房,Candier 和 Stringer 制作咖啡。

49892cf1f41bacfb46d4aadadf9b88b0.png
1*SuZghSKVBqKMuv7E25hWPw.png

Gophers 咖啡馆

在本文中,我将以视觉方式解释 select 语句,这是另一个在Go应用程序中处理并发的强大工具。Gophers 和他们的想象中的咖啡馆仍然会是我的伙伴,但这次,让我们聚焦在 Partier 和订单部分。

场景

Gophers 咖啡馆意识到越来越多的顾客想通过食品外卖应用订购咖啡。因此,除了现场点餐外,他们还选择了一款食品外卖应用。Partier 同时监听来自这两个通道的订单,并将这些订单通过另一个通道 queue 转发给 Candier 和 Stringer。

select {
case order := <-appOrders:
    queue <- order
case order := <-inShopOrders:
    queue <- order
}

当任何一个通道接收到订单时,Partier 会将其转发到 queue 通道。

a30643d7faa8caec4ff3c0f2a954ba0e.png
 
bb2c1d6a145925553262093510ac42d5.png
 

如果两个通道都有订单,其中之一将被选择。在真实的咖啡馆中,来自 inShopOrders 的订单可能会被优先处理。然而,在Go应用程序中,我们不能保证会选择哪个订单。还请注意,select 语句的每次执行只会选取一个订单,Partier 不会先选择一个订单,然后再选择另一个订单。尽管如此,在许多应用程序中,select 语句通常在 for 循环内部,使得前一次迭代中留下的订单在下一次迭代中有机会被选取。

for {
    select {
    case order := <-appOrders:
        queue <- order
    case order := <-inShopOrders:
        queue <- order
    }
}

但是,如果两个通道都有订单,它们将再次进行公平竞争。

04875e5751783e5afef1dfaec1df8735.png
 

默认分支

在非高峰时段,订单不多,Partier 在等待上花费了大量时间。他认为,通过做其他事情,例如清洁桌子,他可以更有成效地利用时间。这可以通过 default 实现。

for {
    select {
    case order := <-appOrders:
        log.Println("There is an order coming from appOrders channel")
        queue <- order
    case order := <-inShopOrders:
        log.Println("There is an order coming from inShopOrders channel")
        queue <- order
    default:
        log.Println("There is no order on both channels, I will do cleaning instead")
        doCleaning()
    }
}

time.After()

通常,time.After(duration) 与 select 一起使用,以防止永远等待。与 default 立即在没有可用通道时执行不同,time.After(duration) 创建一个普通的 <-chan Time,等待 duration 过去,然后在返回的通道上发送当前时间。这个通道在 select 语句中与其他通道一样被处理。正如你所看到的,select 语句中的通道可以是不同类型的。

shouldClose := false
closeHourCh := time.After(8 * time.Hour)


for !shouldClose {
    select {
    case order := <-appOrders:
        log.Println("There is an order coming from appOrders channel")
        queue <- order
    case order := <-inShopOrders:
        log.Println("There is an order coming from inShopOrders channel")
        queue <- order
    case now := <-closeHourCh:
        log.Printf("It is %v now, the shop is closing\n", now)
        shouldClose = true
    default:
        log.Println("There is no order on both channels, I will go cleaning instead")
        doCleaning()
    }
}


log.Println("Shop is closed, I'm going home now. Bye!")

在处理远程API调用时,这种技术非常常见,因为我们不能保证远程服务器何时返回或是否返回。有了 context,我们通常不需要这样做。

responseChannel := make(chan interface{})
timer := time.NewTimer(timeout)


select {
case resp := <-responseChannel:
    log.Println("Processing response")
    processResponse(resp)
    timer.Stop()
case <-timer.C:
    log.Println("Time out, giving up")
}

示例代码

让我们以一个完整的虚构咖啡馆代码结束本文。这里还有一件需要注意的事情,从关闭的通道中选择将总是立即返回。因此,如果您认为有必要,使用“comma ok”习惯用法。亲自动手编码是学习编程的最佳方式。因此,如果您对 select 不太熟悉,我建议您在IDE上复制并尝试修改此代码。

祝您编码愉快!

package main


import (
    "fmt"
    "log"
    "time"
)


func main() {
    appOrders := make(chan order, 3)
    inShopOrders := make(chan order, 3)
    queue := make(chan order, 3)


    go func() {
        for i := 0; i < 6; i++ {
            appOrders <- order(100 + i)
            time.Sleep(10 * time.Second)
        }
        close(appOrders)
    }()


    go func() {
        for i := 0; i < 4; i++ {
            inShopOrders <- order(200 + i)
            time.Sleep(15 * time.Second)
        }
        close(inShopOrders)
    }()


    go partier(appOrders, inShopOrders, queue)


    for o := range queue {
        log.Printf("Served %s\n", o)
    }


    log.Println("Done!")
}


func partier(appOrders <-chan order, inShopOrders <-chan order, queue chan<- order) {
    shouldClose := false
    closeTimeCh := time.After(1 * time.Minute)


    for !shouldClose {
        select {
        case ord, ok := <-appOrders:
            if ok {
                log.Printf("There is %s coming from appOrders channel\n", ord)
                queue <- ord
            }
        case ord, ok := <-inShopOrders:
            if ok {
                log.Printf("There is %s coming from inShopOrders channel\n", ord)
                queue <- ord
            }
        case now := <-closeTimeCh:
            log.Printf("It is %v now, the shop is closing\n", now)
            shouldClose = true
        default:
            log.Println("There is no order on both channels, I will go cleaning instead")
            doCleaning()
        }
    }


    close(queue)
    log.Println("Shop is closed, I'm going home now. Bye!")
}


func doCleaning() {
    time.Sleep(5 * time.Second)
    log.Println("Partier: Cleaning done")
}


type order int


func (o order) String() string {
    return fmt.Sprintf("order-%02d", o)
}

感谢您一直阅读到文章末尾。请考虑关注下作者啦~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT 与 Python进行动态可视化分析

Python数据分析目前最为热门的岗位操作。 想使用Python进行可视化分析&#xff0c;但是又不想写代码&#xff0c;测试&#xff0c;验证。可以交给ChatGPT&#xff0c;open AI 来进行操作。 这样的动态图显示&#xff0c;我们只需要给ChatGPT发送一个指令&#xff0c;人工智能就…

AUTOSAR规范与ECU软件开发(实践篇)6.6 BSW模块代码生成

在BCT界面中配置完所需要的BSW模块后&#xff0c; 可以进行BSW模块相关代码与描述文件的生成&#xff0c; 点击ISOLAR-A主菜单中“ ”右边箭头&#xff0c; 选择Run Configuraions&#xff0c; 如图6.57所示。 将弹出如图6.58所示的界面。 图6.57 Run Configuraions配置&#x…

软件测试 day2

今天目标 能对穷举场景设计测试点 能对限定边界规则设计测试点 能对多条件依赖关系进行设计测试点 能对于项目业务进行设计测试点一、解决穷举场景 重点&#xff1a;使用等价类划分法 1.1 等价类划分法 重点&#xff1a;有效等价和单个无效等价各取1个即可。 步骤&#xff1a;…

R包开发-2.1:在RStudio中使用Rcpp制作R-Package(更新于2023.8.23)

目录 0-前言 1-在RStudio中创建R包项目 2-创建R包 2.1通过R函数创建新包 2.2在RStudio通过菜单来创建一个新包 2.3关于R包创建的说明 3-添加R自定义函数 4-添加C函数 0-前言 目标&#xff1a;在RStudio中创建一个R包&#xff0c;这个R包中包含C函数&#xff0c;接口是Rc…

帆软报表系统SSRF

有子曰&#xff1a;“信近于义&#xff0c;言可复也。恭近礼&#xff0c;远耻辱也。因不失其亲&#xff0c;亦可宗也。” SSRF 构造payload&#xff0c;访问漏洞url&#xff1a; /ReportServer?opresource&resourcehttp://x.x.x漏洞证明&#xff1a; 文笔生疏&#xf…

Redisson分布式锁 原理源码 分析

# 基于setnx实现的分布式锁存在的问题&#xff1a; # 为了解决上面的问题&#xff0c;可以用Redisson # Redisson入门 # Redisson可重入锁原理 获取锁的Lua脚本&#xff1a; 释放锁的Lua脚本&#xff1a; # 锁重试原理分析 tryLock&#xff08;&#xff09;底层代码分析 tim…

在metallb基础上使用 ingress-nginx

vi nginx-ingress.yaml 由于使用了metallb &#xff0c;这里需要把对外暴露service的方式改成 LoadBalancer type: LoadBalancer#type: NodePort apiVersion: v1 kind: Namespace metadata:name: ingress-nginx --- apiVersion: v1 automountServiceAccountToken: true kind…

2022年09月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题:最长上升子序列 一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5…

计算机丢失msvcp110.dll是什么意思?有哪些方法可以修复

今天&#xff0c;我将和大家一起探讨一个关于计算机的问题——“计算机丢失msvcp110.dll是什么意思&#xff1f;有哪些方法可以修复&#xff1f;”这个问题在我们的日常生活中非常常见&#xff0c;尤其是在使用Windows系统的过程中&#xff0c;可能会遇到这样的问题。那么&…

基于Java+SpringBoot+Vue前后端分离体育馆管理系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

代码随想录算法训练营第四十六天|139.单词拆分、多重背包、背包问题总结

139.单词拆分 ★ 文档讲解 &#xff1a; 代码随想录 - 139.单词拆分 状态&#xff1a;再次回顾。&#xff08;★&#xff1a;需要多次回顾并重点回顾&#xff09; 本题其实不套完全背包思路来理解反而更简单易懂一点。 动态规划五部曲&#xff1a; 确定dp数组&#xff08;dp ta…

基于Red Hat Enterprise Linux 7操作系统的PostgresSql15的备份恢复(实践笔记)

零、前言 本文是基于阿里云ECS服务器进行的实践操作&#xff0c;操作系统版本&#xff1a;Red Hat Enterprise Linux 7 PG数据库版本&#xff1a;PostgresSql 15 PG安装方式&#xff1a;yum 由于本人新接触pg数据&#xff0c;本次也是出于好奇&#xff0c;就对pg数据库的pg_du…

二级MySQL(八)——删除表格数据

1、删除特定的数据记录 DELETE FROM tb_student WHERE studentName 黄涛; 删除前&#xff1a; 删除后&#xff1a; 2、带子查询的删除 DELETE FROM tb_student WHERE studentNo (SELECT studentNo FROM tb_student_copy WHERE studentName 孙新); 删除前&#xff1a; 删…

西北大学计算机考研844高分经验分享

西北大学计算机考研844经验分享 个人介绍 ​ 本人是西北大学22级软件工程研究生&#xff0c;考研专业课129分&#xff0c;过去一年里在各大辅导机构任职&#xff0c;辅导考研学生专业课844&#xff0c;辅导总时长达288小时&#xff0c;帮助多名学生专业课高分上岸。 前情回顾…

C++实现客户端/服务端通信(一)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 C实现客户端/服务端通信&#xff08;一&#xff09; 网络编程的基本概念1. 客户端/服务器通信模型&#xff1a;2. socket函数&#xff1a;3. 主机字节序和网络字节序&#xf…

Docker容器学习:搭建自己专属的LAMP环境

目录 编写Dockerfile 1.文件内容需求&#xff1a; 2.值得注意的是centos6官方源已下线&#xff0c;所以需要切换centos-vault源&#xff01; 3.Dockerfile内容 4.进入到 lamp 开始构建镜像 推送镜像到私有仓库 1.把要上传的镜像打上合适的标签 2.登录harbor仓库 3.上传镜…

InDesign软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 InDesign是一款专业的页面设计和布局软件&#xff0c;主要用于印刷和数字出版领域。它是由Adobe公司开发的一款基于文档的排版工具&#xff0c;与Adobe Acrobat、Adobe Illustrator等软件有着相似的用户界面和功能。以下是InDes…

Leetcode 1812。判断国际象棋棋盘中一个格子的颜色

国际棋盘问题&#xff1a; 给你一个坐标 coordinates &#xff0c;它是一个字符串&#xff0c;表示国际象棋棋盘中一个格子的坐标。下图是国际象棋棋盘示意图。 如果所给格子的颜色是白色&#xff0c;请你返回 true&#xff0c;如果是黑色&#xff0c;请返回 false 。 给定坐标…

IDEA软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 IntelliJ IDEA是一款流行的Java集成开发环境&#xff08;IDE&#xff09;&#xff0c;由捷克软件开发公司JetBrains开发。它专为Java开发人员设计&#xff0c;提供了许多高级功能和工具&#xff0c;使得开发人员能够更高效地编写…

最新域名和子域名信息收集技术

域名信息收集 1&#xff0e;WHOIS查询 WHOIS是一个标准的互联网协议&#xff0c;可用于收集网络注册信息、注册域名﹑IP地址等信息。简单来说&#xff0c;WHOIS就是一个用于查询域名是否已被注册及注册域名详细信息的数据库&#xff08;如域名所有人、域名注册商&#xff09;…