💪 三天刷完 Go by Example(中)|Go主题月

581 阅读4分钟

本文为系列文章,Go 基础语法可回顾上篇 三天刷完 Go by Example(上)

Github 代码:github.com/ftopia2020/…

以下仅Go并发的基础语法示例,这些远远不够,你需要结合系统学习其概念和理论、需要大量阅读相关资料和多看多练多理解经典Go并发模式来掌握 Go 并发编程的要义。


  • Do not communicate by sharing memory; instead, share memory by communicating.
  • 不要通过共享内存来通信,而要通过通信来实现内存共享。

以上经典的「Go 并发哲学」可先牢记(不懂没关系),不断的尝试应用会使你更能体会「Go 并发哲学」。

22 Goroutines

当我们运行这个程序时,可能每次运行的结果不同,如截图 going 打印输出于 f("goroutine") 之后

也可能是两个协程的交替输出。 这种交替的情况表示 Go runtime 是以并发的方式运行协程的

$ go run goroutines.go
direct : 0
direct : 1
direct : 2
goroutine : 0
going
goroutine : 1
goroutine : 2
done

对于刚学习 Go 的协程同学来说,可使用 time.Sleep 来使 main 阻塞,使其他协程能够有机会运行完全,但你要注意的是,这并不是推荐的方式(更好的方法是使用 WaitGroup

引申思考

改变 time.Sleep 顺序,多运行几次,为什么结果会有所不同?(有时 goroutine 执行了,有时没有,有时执行了一半??)

一个 Go 程序的入口通常是 main 函数,程序启动后,main 函数最先运行 main goroutine

在 main 中或者其下调用的代码中才可以使用 go + func() 的方法来启动协程。

func main() 地位相当于主线程,当 main 函数执行完成后,这个线程也就终结了,其下的运行着的所有协程也不管代码是不是还在跑,都得退出。

23 Channels

24 Channel Buffering

25 Channel Synchronization

上例使用阻塞接收的方式,实现了等待另一个协程完成

如果需要等待多个协程,WaitGroup 是一个更好的选择

可以尝试下注释 <-done 程序可能在 worker 开始前就结束了

26 Channel Directions

27 Select

select 类似用于通信的 switch 语句,配合 case 使用

  • case 必须是一个通信操作,写/读
  • 支持 default (本例未体现,↓ 会有相关示例)

28 Timeouts

  • 等待时,只有在等待完成时超时case才被执行,在等待过程中,select 一直在等待所有 case
  • 在上面的等待过程中(如 1、3s 等待),其余 case 也一直在持续着

29 Non-blocking Channel Operations

引申思考

如果将两个注释的协程取消注释,再次运行,会打印什么?如果不加超时 time.After 呢?

协程并发,不可确定哪个通道先接收,所以是随机的

非堵塞,default 先行

30 Closing Channels

可尝试向关闭通道写入值看看

31 Range Over Channels

32 Timers

33 Ticker

34 Worker Pools

思考,为什么总共是 2s 左右?如果改写 worker 里面的 time.Sleep 为 2s,总共运行约几秒?

35 Waitgroups

思考,如果注释掉 wg.Wait() 运行结果会是如何?

有关 defer 的理解和使用,请参阅相关资料,如:golang.org/ref/spec#De…

后续下篇也有 defer 的相关示例

36 Rate Limiting

速率限制 是控制服务资源利用和质量的重要机制。 基于协程、通道和打点器,Go 优雅的支持速率限制

运行程序,我们看到第一批请求,大约每 200ms 处理一次

第二批请求(Burst request),可观察运行,瞬间出现 3 个请求,之后约每隔 500ms 处理剩余3 个请求

2500 = 200×5 + 500×3

37 Atomic Counters

感受下Go并发的速度吧 🚀 打印 1000×50 = 50000

38 Mutexes

对于更加复杂的情况,我们可以使用一个*互斥锁(mutexes)* 来在 Go 协程间安全的访问数据

39 Stateful Goroutines

共享的 state 跨多个 Goroutines 同步访问,正好契合「通过通信实现共享内存」的「Go并发哲学」

通过这个例子我们可以看到,基于协程的方法比基于互斥锁的方法要复杂得多。 但是,在某些情况下它可能很有用, 例如,当你涉及其他通道,或者管理多个同类互斥锁时,会很容易出错。 您应该使用最自然的方法,尤其是在理解程序正确性方面。

Next

以上示例,初步展现了 Golang 并发模式的魅力~

初学者或者并发编程基础较薄弱的开发理解起来会有点吃力,继续加油 💪

之后下篇,是一些 Golang 具体开发场景的示例,包含以下内容:

  • 一些内置包的常用方法(如:sort, time, io, os, fmt 等)
  • Panic
  • Defer
  • 单元测试
  • 命令行操作
  • HTTP 基础
  • 生成进程 / 执行进程
  • 信号
  • 退出