Golang并发模型:select进阶

前一篇文章《Golang并发模型:轻松入门select》介绍了select的作用和它的基本用法,这次介绍它的3个进阶特性。

  1. nil的通道永远阻塞
  2. 如何跳出for-select
  3. select{}阻塞

nil的通道永远阻塞

case上读一个通道时,如果这个通道是nil,则该case永远阻塞。这个功能有1个妙用,select通常处理的是多个通道,当某个读通道关闭了,但不想select再继续关注此case,继续处理其他case,把该通道设置为nil即可。
下面是一个合并程序等待两个输入通道都关闭后才退出的例子,就使用了这个特性。

func combine(inCh1, inCh2 <-chan int) <-chan int {
    // 输出通道
    out := make(chan int)

    // 启动协程合并数据
    go func() {
        defer close(out)
        for {
            select {
            case x, open := <-inCh1:
                if !open {
                    inCh1 = nil
                    continue
                }
                out<-x
            case x, open := <-inCh2:
                if !open {
                    inCh2 = nil
                    continue
                }
                out<-x
            }

            // 当ch1和ch2都关闭是才退出
            if inCh1 == nil && inCh2 == nil {
                break
            }
        }
    }()

    return out
}

如何跳出for-select

breakselect内的并不能跳出for-select循环。看下面的例子,consume函数从通道inCh不停读数据,期待在inCh关闭后退出for-select循环,但结果是永远没有退出。

func consume(inCh <-chan int) {
    i := 0
    for {
        fmt.Printf("for: %d\n", i)
        select {
        case x, open := <-inCh:
            if !open {
                break
            }
            fmt.Printf("read: %d\n", x)
        }
        i++
    }

    fmt.Println("combine-routine exit")
}

运行结果:

? go run x.go
for: 0
read: 0
for: 1
read: 1
for: 2
read: 2
for: 3
gen exit
for: 4
for: 5
for: 6
for: 7
for: 8
... // never stop

既然break不能跳出for-select,那怎么办呢?给你3个锦囊:

  1. 在满足条件的case内,使用return,如果有结尾工作,尝试交给defer
  2. selectfor内使用break挑出循环,如combine函数。
  3. 使用goto

select{}永远阻塞

select{}的效果等价于创建了1个通道,直接从通道读数据:

ch := make(chan int)
<-ch

但是,这个写起来多麻烦啊!没select{}简洁啊。
但是,永远阻塞能有什么用呢!?
当你开发一个并发程序的时候,main函数千万不能在子协程干完活前退出啊,不然所有的协程都被迫退出了,还怎么提供服务呢?
比如,写了个Web服务程序,端口监听、后端处理等等都在子协程跑起来了,main函数这时候能退出吗?

select应用场景

最后,介绍下我常用的select场景:

  1. 无阻塞的读、写通道。即使通道是带缓存的,也是存在阻塞的情况,使用select可以完美的解决阻塞读写,这篇文章我之前发在了个人博客,后面给大家介绍下。
  2. 给某个请求/处理/操作,设置超时时间,一旦超时时间内无法完成,则停止处理。
  3. select本色:多通道处理

原文:https://studygolang.com/articles/17055?fr=sidebar

并发系列文章推荐

    1. Golang并发模型:轻松入门流水线模型
    2. Golang并发模型:轻松入门流水线FAN模式
    3. Golang并发模型:并发协程的优雅退出
    4. Golang并发模型:轻松入门select

原文地址:https://www.cnblogs.com/show58/p/12706679.html

时间: 2024-10-17 14:10:14

Golang并发模型:select进阶的相关文章

Golang并发中channel的分析

问题:面对并发问题,是用channel解决,还是用Mutex解决? 如果自己心里还没有清晰的答案,那就读下这篇文章,你会了解到: 使用channel解决并发问题的核心思路和示例 channel擅长解决什么样的并发问题,Mutex擅长解决什么样的并发问题 一个并发问题该怎么入手解解决 一个重要的plus思维 前戏 前面很多篇的文章都在围绕channel介绍,而只有前一篇sync的文章介绍到了Mutex,不是我偏心,而是channel在Golang是first class级别的,设计在语言特性中的,

golang中的CSP并发模型

1. 相关概念: 用户态:当一个进程在执行用户自己的代码时处于用户运行态(用户态) 内核态:当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),引入内核态防止用户态的程序随意的操作内核地址空间,具有一定的安全保护作用.这种保护模式是通过内存页表操作等机制,保证进程间的地址空间不会相互冲突,一个进程的操作不会修改另一个进程地址空间中的数据. 用户态与内核态之间的切换:当在系统中执行一个程序时,大部分时间都是运行在用户态下的,在其需要操作系统帮助完成一些用户态自己没有特权和能力完成的

golang常见的几种并发模型框架

原文链接 package main import ( "fmt" "math/rand" "os" "runtime" "sync" "sync/atomic" "time" ) type Scenario struct { Name string Description []string Examples []string RunExample func() } v

马蜂窝搜索基于 Golang 并发代理的一次架构升级

搜索业务是马蜂窝流量分发的重要入口.很多用户在使用马蜂窝时,都会有目的性地主动搜索与自己旅行需求相关的各种信息,衣食住行,事无巨细,从而做出最符合需求的旅行决策. 因此在马蜂窝,搜索业务交互的下游模块非常多,主要有目的地.POI.热门景点.美食.商场.酒店.问答.攻略.机票火车票等等,通过实时.精准地返回搜索结果,帮助用户做出个性化旅行决策. 面对越来越高的流量,马蜂窝技术团队积极尝试对搜索架构进行优化和升级,来保证搜索业务的稳定和性能. 方案背景 由于历史原因,优化前的搜索服务与下游模块交的互

golang并发编程

golang并发编程 引子 golang提供了goroutine快速实现并发编程,在实际环境中,如果goroutine中的代码要消耗大量资源时(CPU.内存.带宽等),我们就需要对程序限速,以防止goroutine将资源耗尽.以下面伪代码为例,看看goroutine如何拖垮一台DB.假设userList长度为10000,先从数据库中查询userList中的user是否在数据库中存在,存在则忽略,不存在则创建. //不使用goroutine,程序运行时间长,但数据库压力不大 for _,v:=ra

用 Python 理解 Web 并发模型

用 Python 理解 Web 并发模型 http://www.jianshu.com/users/1b1fde012122/latest_articles 来源:MountainKing 链接:http://www.jianshu.com/p/80feb3bf5c70# 前言 虽然异步是我们急需掌握的高阶技术,但是不积跬步无以至千里,同步技术的学习是不能省略的.今天这篇文章主要用Python来介绍Web并发模型,直观地展现同步技术的缺陷以及异步好在哪里. 最简单的并发 import socke

并发模型与IO模型梳理

并发模型 常见的并发模型一般包括3类,基于线程与锁的内存共享模型,actor模型和CSP模型,其中尤以线程与锁的共享内存模型最为常见.由于go语言的兴起,CSP模型也越来越受关注.基于锁的共享内存模型与后两者的主要区别在于,到底是通过共享内存来通信,还是通过通信来实现访问共享内存.由于actor模型和CSP模型,本人并不是特别了解,我主要说说最基本的并发模型,基于线程与锁的内存共享模型. 为什么要并发,本质都是为了充分利用多核CPU资源,提高性能.但并发又不能乱,为了保证正确性,需要通过共享内存

模型层进阶相关

模型层进阶相关 选择合适的层级工作 要在对应的level(MVC) 做对应的事. 例如计算 count, 在最低的数据库 level 里是最快的 (如果只需要知道此记录是否存在的话,用 exists() 会更快). 但要 注意: queryset 是 lazy 的,所以有时候在 higher level (例如模板) 里控制 queryset 是否真的执行,说不定会更高效. 下面这段代码很好的解释了不同 level 的意思: # QuerySet operation on the databas

课堂笔记--Strom并发模型

Strom并发模型: topology是如何运行的?(可与mapreduce对比) 第一层:cluster 第二层:supervisor(host.node.机器) 第三层:worker(进程) 第四层:executor(线程) 第五层:task(线程的一个个对象.如Spout和Blot) //topology:N workers==每个==>1 excutor=>1 task //此时,它和mapreduce那个是一样的 topology代码    conf.setNumWorkers(2)