0%

之前的文章都提到过,Golang的并发模型都来自生活,select也不例外。举个例子:我们都知道一句话,“吃饭睡觉打豆豆”,这一句话里包含了3件事:

  1. 妈妈喊你吃饭,你去吃饭。
  2. 时间到了,要睡觉。
  3. 没事做,打豆豆。

在Golang里,select就是干这个事的:到吃饭了去吃饭,该睡觉了就睡觉,没事干就打豆豆。

结束发散,我们看下select的功能,以及它能做啥。

select功能

在多个通道上进行读或写操作,让函数可以处理多个事情,但1次只处理1个。以下特性也都必须熟记于心:

  1. 每次执行select,都会只执行其中1个case或者执行default语句。
  2. 当没有case或者default可以执行时,select则阻塞,等待直到有1个case可以执行。
  3. 当有多个case可以执行时,则随机选择1个case执行。
  4. case后面跟的必须是读或者写通道的操作,否则编译出错。

select长下面这个样子,由selectcase组成,default不是必须的,如果没其他事可做,可以省略default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
readCh := make(chan int, 1)
writeCh := make(chan int, 1)

y := 1
select {
case x := <-readCh:
fmt.Printf("Read %d\n", x)
case writeCh <- y:
fmt.Printf("Write %d\n", y)
default:
fmt.Println("Do what you want")
}
}

我们创建了readChwriteCh2个通道:

  1. readCh中没有数据,所以case x := <-readCh读不到数据,所以这个case不能执行。
  2. writeCh是带缓冲区的通道,它里面是空的,可以写入1个数据,所以case writeCh <- y可以执行。
  3. case可以执行,所以default不会执行。

这个测试的结果是

1
2
$ go run example.go
Write 1

用打豆豆实践select

来,我们看看select怎么实现打豆豆:eat()函数会启动1个协程,该协程先睡几秒,事件不定,然后喊你吃饭,main()函数中的sleep是个定时器,每3秒喊你吃1次饭,select则处理3种情况:

  1. eatCh中读到数据,代表有人喊我吃饭,我要吃饭了。
  2. sleep.C中读到数据,代表闹钟时间到了,我要睡觉。
  3. default是,没人喊我吃饭,也不到时间睡觉,我就打豆豆。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import (
"fmt"
"time"
"math/rand"
)

func eat() chan string {
out := make(chan string)
go func (){
rand.Seed(time.Now().UnixNano())
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
out <- "Mom call you eating"
close(out)
}()
return out
}


func main() {
eatCh := eat()
sleep := time.NewTimer(time.Second * 3)
select {
case s := <-eatCh:
fmt.Println(s)
case <- sleep.C:
fmt.Println("Time to sleep")
default:
fmt.Println("Beat DouDou")
}
}

由于前2个case都要等待一会,所以都不能执行,所以执行default,运行结果一直是打豆豆:

1
2
$ go run x.go
Beat DouDou

现在我们不打豆豆了,你把default和下面的打印注释掉,多运行几次,有时候会吃饭,有时候会睡觉,比如这样:
1
2
3
4
5
6
$ go run x.go
Mom call you eating
$ go run x.go
Time to sleep
$ go run x.go
Time to sleep

select很简单但功能很强大,它让golang的并发功能变的更强大。这篇文章写的啰嗦了点,重点是为下一篇文章做铺垫,下一篇我们将介绍下select的高级用法。

select的应用场景很多,让我总结一下,放在下一篇文章中吧。

完整代码

可在Github查看:https://github.com/Shitaibin/golang_step_by_step/tree/master/golang_select

并发系列文章推荐

  1. 如果这篇文章对你有帮助,不妨关注下我的Github,有文章会收到通知。
  2. 本文作者:大彬
  3. 如果喜欢本文,随意转载,但请保留此原文链接:https://mp.weixin.qq.com/s/ACh-TGlPo72r4e6pbh52vg

一起学Golang-分享有料的Go语言技术

公众号:一起学Golang,分享有料的Go语言技术。以下是公众号历史文章,希望对你有用:

  1. Golang并发模型:轻松入门流水线模型
  2. Golang并发模型:轻松入门流水线FAN模式
  3. Golang并发模型:并发协程的优雅退出
  4. Golang并发模型:轻松入门select
  5. Golang并发模型:select进阶
  6. Golang并发模型:轻松入门协程池
  7. Golang并发的次优选择:sync包
  8. Golang并发:再也不愁选channel还是选锁
阅读全文 »

区块链就是何交易打交道,我们今天就介绍下,交易处理过程中的一个重要组成部分:txpool。这篇文章主要从功能角度介绍,通过这篇文章会了解:

  1. txpool的在交易中的位置和作用。
  2. txpool的功能,核心组成部分queued和pending。
  3. txpool如何实现它的功能。
  4. txpool源码的重要关注点。
阅读全文 »

goroutine作为Golang并发的核心,我们不仅要关注它们的创建和管理,当然还要关注如何合理的退出这些协程,不(合理)退出不然可能会造成阻塞、panic、程序行为异常、数据结果不正确等问题。这篇文章介绍,如何合理的退出goroutine,减少软件bug。

goroutine在退出方面,不像线程和进程,不能通过某种手段强制关闭它们,只能等待goroutine主动退出。但也无需为退出、关闭goroutine而烦恼,下面就介绍3种优雅退出goroutine的方法,只要采用这种最佳实践去设计,基本上就可以确保goroutine退出上不会有问题,尽情享用。

1:使用for-range退出

for-range是使用频率很高的结构,常用它来遍历数据,range能够感知channel的关闭,当channel被发送数据的协程关闭时,range就会结束,接着退出for循环。

阅读全文 »

前一篇文章《Golang并发模型:轻松入门流水线模型》,介绍了流水线模型的概念,这篇文章是流水线模型进阶,介绍FAN-IN和FAN-OUT,FAN模式可以让我们的流水线模型更好的利用Golang并发,提高软件性能。但FAN模式不一定是万能,不见得能提高程序的性能,甚至还不如普通的流水线。我们先介绍下FAN模式,再看看它怎么提升性能的,它是不是万能的。

FAN-IN和FAN-OUT模式

Golang的并发模式灵感来自现实世界,这些模式是通用的,毫无例外,FAN模式也是对当前世界的模仿。以汽车组装为例,汽车生产线上有个阶段是给小汽车装4个轮子,可以把这个阶段任务交给4个人同时去做,这4个人把轮子都装完后,再把汽车移动到生产线下一个阶段。这个过程中,就有任务的分发,和任务结果的收集。其中任务分发是FAN-OUT,任务收集是FAN-IN。

阅读全文 »

Golang中我们使用Channel或者sync.Mutex等锁保护数据,有没有一种机制可以检测代码中的数据竞争呢?

背景知识

数据竞争是并发情况下,存在多线程/协程读写相同数据的情况,必须存在至少一方写。另外,全是读的情况下是不存在数据竞争的。

使用race检测数据竞争

go build有个标记race可以帮助检测代码中的数据竞争。

1
2
3
4
5
➜  awesome git:(master) ✗ go help build
//.... omit
-race
enable data race detection.
Supported only on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64.
阅读全文 »

网上写govendor的博文不少,但从安装到介绍,总看上去有些沉重,下面奉上一篇简单的教程,3分钟入门。

第1部分 简明教程

2步走,3分钟轻松搞定Go项目的依赖。

第1步 安装

1
go get -u github.com/kardianos/govendor
阅读全文 »

Golang作为一个实用主义的编程语言,非常注重性能,在语言特性上天然支持并发,它有多种并发模型,通过流水线模型系列文章,你会更好的使用Golang并发特性,提高你的程序性能。

这篇文章主要介绍流水线模型的流水线概念,后面文章介绍流水线模型的FAN-IN和FAN-OUT,最后介绍下如何合理的关闭流水线的协程。

Golang的并发核心思路

Golang并发核心思路是关注数据流动。数据流动的过程交给channel,数据处理的每个环节都交给goroutine,把这些流程画起来,有始有终形成一条线,那就能构成流水线模型。

但我们先从简单的入手。

阅读全文 »

你是不是觉得defer很简单、很好用,但也许你掉坑里了都不知道!

这篇文章不介绍defer的常用功能,而是介绍你在用defer时,也许会踩的坑。

defer允许我们进行一些函数执行完成后的收尾工作,并且代码更加简洁,例如:

  1. 关闭文件流:

    1
    2
    // open a file
    defer file.Close()
阅读全文 »

网上已搜索golang pprof,资料不少,简明高效的一个没看到,这篇文章5步教你用会pprof获取cpu和内存prof。

第1步:安装易用的pprof

golang自带的prof包是runtime/pprof,这个是低级别的,需要你手动做一些设置等等周边工作,不利于我们快速上手,利用pprof帮助我们解决实际的问题。这里推荐davecheney封装的pprof,它可以1行代码,让你用上pprof,专心解决自己的代码问题,下载:

1
go get github.com/pkg/profile
阅读全文 »