你好,我是小小酥,在上一篇文章中,我们介绍了go语言中的接口、函数和结构体,也对go语言中常用的fmt包进行了介绍,今天我们将继续介绍go语言中核心的知识点,go并发编程,准备发车~
并行/并发在第一课我们有提到,go语言是一门面向并发的语言。
并行和并发是我们工作中经常听到的词汇,那么在学习go语言的并发编程之前,我们需要先了解并行与并发的区别。
举个现实生活中的例子。
有一个高富帅,挺有钱的,他有很多女朋友,并且女孩子们都知道对方的存在,所以他能够同时和多个女生进行约会,那么我们将他的行为称为并行约会。
还有一个渣男B,没有钱但是挺会骗人的,他也有很多女朋友,但是女孩子们并不知道对方的存在,所以渣男B一次只能和一个女生进行约会,每天换着和不同的女孩子进行约会,那么我们将他的行为称为并发约会。
总结一下,在计算机的世界里,我们单核的CPU同一时间实际只能执行一个线程,但是通过操作系统对线程的调度,多个程序能被交替运行,我们将之称为并发。
如果我们是多核的CPU,同一时间最多可以有多个线程被同时运行,我们就将之称为并行。
进程/线程/协程了解了并行和并发的区别,在学习go并发编程之前,我们还需要弄清楚三个概念,进程、线程和协程。
进程进程是对运行程序的抽象,是操作系统分配资源的基本单位,多个进程之间资源相互隔离。
线程虽然操作系统通过系统调度,可以对多个进程并发进行调度,但是由于进程之间完全隔离,因此导致调度时上下文切换成本较高,效率较低。比如一个视频播放器,对于播放声音和播放视频是两个进程,由于进程之间的切换成本较高,就可能会出现声画不同步的问题。
因此线程诞生了,线程是进程的子单位,一个进程可以拥有多个线程,多个线程之间共享同一内存地址空间,代码段,全局变量等等。因此线程比进程更轻量,线程之间切换时需要存储和还原的资源也就更少,所以多线程之间切换的性能是优于多进程的,线程同时也是操作系统调度的基本单位。
协程协程,也被称为用户态线程,比起线程,有着更加轻量以及由用户态进行调度的优势。
因为在用户态进行调度,所以协程之间切换时开销更小,性能更高。
因为协程占用的资源更少,因此相比线程可以同时创建出更多的协程用于处理请求。
不同语言对协程的实现与调度都不尽相同。
比如在python中,通过yiled关键字来创建一个协程,协程之间的调度是非抢占式的,也就是一个协程必须主动出让执行机会,其他协程才有机会运行。
而在go语言中,我们通过go关键字来创建一个协程,通过go运行时的全局监控,实现了抢占式的调度。
启动一个协程packagemainfuncmain(){}
在go语言中,所有程序都运行在协程(goroutine)中,比如我们上面这段代码,main函数就被运行在一个goroutine中,我们将其称为maingoroutine,所有其他的goroutine在maingoroutine执行完毕时会一并结束。
packagemainimport"fmt"funcmain(){//创建协程1gopush()//创建协程2gofunc(){fmt.Println("创建一个协程")}()}funcpush(){fmt.Println("发送一条推送")}
除了maingoroutine,我们通过gofunc(){}()或者go函数名()就可以启动一个协程,是不是很简单?
餐后甜点在前面的文章中,有读者反馈说一篇文章的知识点太多了,一次性会吸收不过来,在采纳了大家的建议后,后面的文章我们会争取将各个知识点的粒度拆的更细,以减少每篇文章的篇幅,让大家能更好的去掌握每个知识点。
在今天文章的最后,希望你思考一下下面这段代码运行后会输出什么呢?期待你在后台留言给我答复~
packagemainimport"fmt"funcmain(){gofunc(){fmt.Println("hello")}gofunc(){fmt.Println("world")}}下期预告
今天我们学习了并行与并发;进程,线程与协程;以及go语言中如何创建一个协程。
下一次我们会继续对go语言中协程之间如何通信进行讲解,敬请期待~
如果你喜欢我的文章,欢迎