聊聊Go语言并发之道

在这里插入图片描述

写在前面

  2007年,Go语言诞生于Google公司,2009年开源,2012年推出1.0版本,曾两次获得TIOBE年度语言。2012年起,全球大量的开源项目开始使用Go语言开发,目前Go语言已成为云计算领域事实上的标准语言,特别是在容器领域,诞生了一大批优秀的开源软件,如Docker,Kubernetes等。2017年区块链技术在国内大热,区块链两个大的技术平台以以太坊(Ethereum)和超级账本(Hyperledger)子项目Fabric都是基于Go语言构建的。Go语言的应用领域逐渐扩大,目前的区块链、云计算、中间件和服务器编程领域显现出明显的优势。Go语言最先在云计算盛行,随后大量的互联网初创企业将Go语言作为后台主要开发语言。目前,无论互联网公司的独角兽,还是BAT,都已将Go语言作为其技术栈的重要组成部分。市场对Go语言编程人才的需求量也在持续上升。所以今天聊聊Go语言的核心,并发。

什么是并发和并行

  当前环境下,CPU的工艺制程已经实现了7nm商用。可以预测单颗CPU的性能已经很难有指数级的提升,未来多核是主要的发展方向。硬件对软件的影响显而易见,软件的并发和并行也是未来的发展方向。并发和并行是两个不同的概念,应用程序具备好的并发结构,操作系统才能更好地利用硬件并行执行,同时避免阻塞等待,合理的进行调度,提高CPU利用率。

  • 并行意味着程序在任何时刻都是同时运行的,并行就是在任一粒度的时间内部具备同时执行的能力,具有瞬时性,在于执行。
  • 并发意味着程序在单位时间内是同时运行的,并发实在规定时间内多个请求都能得到执行和处理,具有过程性,在于结构。

GoRoutine

  操作系统本身可以进行线程和进程的调度,本身具备并发能力,但进程切换代价过于高昂,进程切换需要保存现场,耗费较多的时间。Go语言的并发思想就是让应用程序在用户层再构建一级调度将并发的粒度进一步降低,以更大限度地提高程序运行的效率呢。
  Go语言的并发执行体称为goroutine,routine的意思就是例程,所以goroutine叫go例程更合理,我还是习惯直接叫goroutine。

Go语言调度模型

  CPU执行指令的速度是非常快的,大部分简单的指令执行仅需三分之一纳秒,即1s可以执行30亿条简单指令,这个速度已经非常快了,CPU慢在对外部数据的读/写上,外部I/O的速度慢和阻塞是导致CPU使用效率不高的最大原因。在真实的系统中,CPU从来不是瓶颈,而是大部分的时间都被浪费了,所以我们需要增加CPU的有效吞吐量,尽可能让每个CPU核心都有事情做,尽可能提高每个CPU做事的效率。
  应用程序的并发模型是多样的,其中最高效的还是协程,Go的并发执行模型就是一种变种的协程模型。

MPG模型

Go语言天生支持并发,MPG模型是go语言的并发模型

在这里插入图片描述

  • G - Goroutine: Go运行时对goroutine的抽象描述,存放兵法执行的代码入口地址、上下文、运行环境、运行栈等执行相关的元信息。
  • M - Machine:OS内核线程,是操作系统层面调度和执行的实体,负责执行。
  • P - Processor:M运行G时所需要的资源,对资源的一种抽象和管理,P不是一段代码实体,而是一个管理的数据结构, P主要降低M管理调度G的复杂性,增加一个间接的控制层数据结构。

Goroutine特点

  • 有独立的栈空间,共享程序堆空间
  • 调度由用户控制,不能保证多个goroutine的执行顺序
  • goroutine是一个轻量级的用户态的轻量级线程
  • goroutine从主线程开启的,是轻量级的线程,是逻辑态,对资源消耗相对小
  • goroutine可以轻松开启上万个线程。其他编程语言的并发机制是一般基于线程的,开启过多的线程,资源耗费大,这里更加可以体现出Go语言在并发上的优势了

Goroutine使用

普通函数启动goroutine

  代码中使用time模块阻塞了主进程,等待所有goroutine执行完毕后再结束程序,但是这样我们并不知道goroutine何时执行完毕,即对时间的把控没有一个参考,在go语言中,sync提供多个Goroutine同步机制,主要通过WaitGroup实现。

package main

import (
	"fmt"
	"time"
)

// 计算1-100的和
func calc() {
	total := 0
	for i := 0; i <= 100; i++ {
		total += i
	}

	fmt.Printf("%v", total)
}

func main() {
	go calc()

	// 阻塞主进程
	time.Sleep(time.Second)
}

WaitGroup

  sync包提供了多个Goroutine同步机制,主要通过WaitGroup实现。下面代码开启5个Goroutine,

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

// 计算1-100的和
func calc() {
  // wg.Done()方法等价于 wg.Add(-1)
	defer wg.Done()

	total := 0
	for i := 0; i <= 100; i++ {
		total += i
	}

	fmt.Printf("%v\n", total)
}

func main() {

	for i := 0; i < 5; i++ {
    // 每启动一个Goroutine,同时给wg加1
		wg.Add(1)
		go calc()
	}
	
  // 等待所有Goroutine运行完成
	wg.Wait()
}

Chan

  chan是Go语言的一个关键字,是channel的简写,goroutine是Go语言里面的并发执行体,chan是goroutine之间通信和同步的重要组件。然而在Go语言提倡通过通信来共享内存
  通道分为无缓冲通道和有缓冲通道,无缓冲通道的len和cap都为0。无缓冲通道既可以用于通信,也可以用于两个Goroutine之间同步,有缓冲通道主要用于通信。

使用无缓冲通道实现Goroutine之间同步等待:

package main

import "fmt"

// 计算1-100的和
func calc(c chan bool) {
	total := 0
	for i := 0; i <= 100; i++ {
		total += i
	}
  fmt.Printf("%v", total)
  
	c <- true
}

func main() {
  // 声明一个bool类型的无缓冲通道用来同步等待
	c := make(chan bool)
	go calc(c)

	// 同步等待
	<-c
}

总结

  Goroutine的工作方式,就是多个协程在多个线程上切换,既可以用到多核,又可以减少切换开销。不需要研发过度参与,提高代码效率,在研发过程中继续探寻吧。。。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
本书作者带你一步一步深入这些方法。你将理解 Go语言为何选定这些并发模型,这些模型又会带来什么问题,以及你如何组合利用这些模型中的原语去解决问题。学习那些让你在独立且自信的编写与实现任何规模并发系统时所需要用到的技巧和工具。 理解Go语言如何解决并发难以编写正确这一根本问题。 学习并发与并行的关键性区别。 深入到Go语言的内存同步原语。 利用这些模式中的原语编写可维护的并发代码。 将模式组合成为一系列的实践,使你能够编写大规模的分布式系统。 学习 goroutine 背后的复杂性,以及Go语言的运行时如何将所有东西连接在一起。 作者简介 · · · · · · Katherine Cox-Buday是一名计算机科学家,目前工作于 Simple online banking。她的业余爱好包括软件工程、创作、Go 语言(igo、baduk、weiquei) 以及音乐,这些都是她长期的追求,并且有着不同层面的贡献。 目录 · · · · · · 前言 1 第1章 并发概述 9 摩尔定律,Web Scale和我们所陷入的混乱 10 为什么并发很难? 12 竞争条件 13 原子性 15 内存访问同步 17 死锁、活锁和饥饿 20 确定并发安全 28 面对复杂性的简单性 31 第2章 对你的代码建模:通信顺序进程 33 并发与并行的区别 33 什么是CSP 37 如何帮助你 40 Go语言并发哲学 43 第3章 Go语言并发组件 47 goroutine 47 sync包 58 WaitGroup 58 互斥锁和读写锁 60 cond 64 once 69 池 71 channel 76 select 语句 92 GOMAXPROCS控制 97 小结 98 第4章 Go语言并发模式 99 约束 99 for-select循环103 防止goroutine泄漏 104 or-channel 109 错误处理112 pipeline 116 构建pipeline的最佳实践 120 一些便利的生成器 126 扇入,扇出 132 or-done-channel 137 tee-channel 139 桥接channel模式 140 队列排队143 context包 151 小结 168 第5章 大规模并发 169 异常传递169 超时和取消 178 心跳 184 复制请求197 速率限制199 治愈异常的goroutine 215 小结 222 第6章 goroutine和Go语言运行时 223 工作窃取223 窃取任务还是续体 231 向开发人员展示所有这些信息 240 尾声 240 附录A 241

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楼下安同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值