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


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

    Go并发可视化解释 — Channel

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

    4a8b8c43a8b79926b670c3d1e8796628.png

    Gophers' Cafe(Gopher咖啡馆)

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

    情景

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

    1. select {
    2. case order := <-appOrders:
    3. queue <- order
    4. case order := <-inShopOrders:
    5. queue <- order
    6. }

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

    fe6d479e742783133996ee53b4a09910.png 69b1d1c2bc704632ec8aa92d99d6ab50.png

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

    1. for {
    2. select {
    3. case order := <-appOrders:
    4. queue <- order
    5. case order := <-inShopOrders:
    6. queue <- order
    7. }
    8. }

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

    5bcb1c847f1f6cec1d336f9b5758b175.png

    默认情况(Default)

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

    1. for {
    2. select {
    3. case order := <-appOrders:
    4. log.Println("There is an order coming from appOrders channel")
    5. queue <- order
    6. case order := <-inShopOrders:
    7. log.Println("There is an order coming from inShopOrders channel")
    8. queue <- order
    9. default:
    10. log.Println("There is no order on both channels, I will do cleaning instead")
    11. doCleaning()
    12. }
    13. }

    time.After()

    time.After(duration)通常与select一起使用,以防止永久等待。与default不同,time.After(duration)会创建一个普通的<-chan Time,等待duration时间的流逝,然后将当前时间发送到返回的通道上。这个通道在select语句中与其他通道平等对待。正如你所看到的,select语句中的通道可以是不同类型的。

    1. shouldClose := false
    2. closeHourCh := time.After(8 * time.Hour)
    3. for !shouldClose {
    4. select {
    5. case order := <-appOrders:
    6. log.Println("There is an order coming from appOrders channel")
    7. queue <- order
    8. case order := <-inShopOrders:
    9. log.Println("There is an order coming from inShopOrders channel")
    10. queue <- order
    11. case now := <-closeHourCh:
    12. log.Printf("It is %v now, the shop is closing\n", now)
    13. shouldClose = true
    14. default:
    15. log.Println("There is no order on both channels, I will go cleaning instead")
    16. doCleaning()
    17. }
    18. }
    19. log.Println("Shop is closed, I'm going home now. Bye!")

    当处理远程API调用时,这种技术非常常见,因为我们无法保证远程服务器何时返回或是否返回。借助于context,通常不需要这样做。

    1. responseChannel := make(chan interface{})
    2. timer := time.NewTimer(timeout)
    3. select {
    4. case resp := <-
  • 相关阅读:
    1024程序员节日:庆祝代码世界的创造者与守护者
    Matlab导入数据(一定有用!!)
    什么是 PowerShell?
    在 CSS 中,怎样有效地优化样式表的加载性能,减少页面加载时间?
    charles劫持修改js文件
    FPGA 之 时序分析
    【设计模式】23种设计模式笔记
    Java基础知识点整理(2022年最新版)
    大话STL第六期——map/multimap
    探讨基于IEC61499开发类似LabVIEW图形编程工具
  • 原文地址:https://blog.csdn.net/weixin_37604985/article/details/132913768