Go并发可视化解释 – select语句

上周,我发布了一篇关于如何直观解释Golang中通道(Channel)的文章。如果你对通道仍然感到困惑,请先查看那篇文章。

Go并发可视化解释 --- Channel

作为一个快速复习:Partier、Candier和Stringer经营着一家咖啡店。Partier负责接受顾客的订单,然后将这些订单传递给厨房,Candier和Stringer制作咖啡。

Gophers' Cafe(Gopher咖啡馆)

在本文中,我将直观解释select语句,这是在Go应用程序中处理并发的另一个强大工具。Gophers和他们的虚构咖啡馆仍然是我的伙伴,但这次,让我们聚焦在Partier和点单部分。

情景

Gopher的Cafe意识到越来越多的顾客希望通过外卖应用程序在线订购咖啡。因此,除了店内点餐外,他们还选择了一个外卖应用程序。Partier会监视来自两个通道的订单,并通过另一个名为queue的通道将这些订单转发给Candier和Stringer。

go 复制代码
select {
case order := <-appOrders:
    queue <- order
case order := <-inShopOrders:
    queue <- order
}

当这两个通道中的任何一个有订单时,Partier会获取订单并将其转发到queue通道。

如果这两个通道都有订单,将会选择其中一个。在实际的咖啡店中,来自inShopOrders的订单可能会被优先处理。但是,在Go应用程序中,我们无法保证哪个订单会被选择。还要注意,select语句的执行只会选择一个订单,Partier不会一次选择两个订单。但是,在许多应用程序中,select语句通常嵌套在for循环中,以便在前一个迭代中剩下的订单有机会在下一个迭代中被选择。

go 复制代码
for {
    select {
    case order := <-appOrders:
        queue <- order
    case order := <-inShopOrders:
        queue <- order
    }
}

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

默认情况(Default)

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

go 复制代码
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语句中的通道可以是不同类型的。

go 复制代码
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,通常不需要这样做。

go 复制代码
responseChannel := make(chan interface{})
timer := time.NewTimer(timeout)


select {
case resp := <-
相关推荐
程序员爱钓鱼12 分钟前
Rust 函数与返回值详解:参数、表达式与返回类型
后端·rust
这不小天嘛5 小时前
JAVA八股——J集合篇
java·开发语言
AOwhisky6 小时前
Python 学习笔记(第一期与第二期)——基础语法——核心知识点自测与详解
开发语言·笔记·python·学习·云原生·运维开发
tiancaijiben6 小时前
阿里云SSL证书自动续签与部署完全指南:从托管服务到开源工具全方案解析
数据库
全堆鸿蒙6 小时前
25 数据库设计与表结构规划:5 张表的设计思路
数据库·华为·cocoa·harmonyos
笃行3507 小时前
【金仓数据库产品体验官】_坏得起_实测:备份恢复、PITR 时间穿越与主备切换——KingbaseES V9R3C18 深度体验(三)
数据库
喜欢的名字被抢了8 小时前
Python实战:SQLAlchemy ORM与FastAPI项目集成
开发语言·python·sql·教程·fastapi
笃行3509 小时前
从兼容到进阶:驱动直连、窗口函数与动态 SQL 实测——KingbaseES V9R3C18 深度体验(二)
数据库
笃行3509 小时前
MySQL 的 JSON 字段迁到金仓还能用吗?——KingbaseES V9R3C18 JSON 操作符与函数兼容实测
数据库
IpdataCloud10 小时前
跨境AI金融风险怎么防?IP风险画像的实战应用指南
数据库·tcp/ip·金融·ip