
🏷️个人主页:鼠鼠我捏,要死了捏的主页
🏷️系列专栏:Golang全栈-专栏
🏷️个人学习笔记,若有缺误,欢迎评论区指正
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站AI学习网站。
前言
当我们开发一个Web服务时,我们希望可以同时处理成千上万的用户请求,当我们有大量数据要计算时,我们希望可以同时开启多个任务进行处理,随着硬件性能的提升以及应用数据的增长,有越来越多的场景需要高并发处理,而高并发是Go的强项。
在这篇文章中,我们就一起来探究一下Go并发编程!
目录
前言
并发与并行
并发
并行
Goroutines
什么是Goroutine
Goroutine的优势
启动Goroutine
关闭Goroutine
Channel
什么是Channel
创建Channel
Channel操作
发送与接收
关闭
遍历
无缓冲区Channel
有缓冲区Channel
Channel的串联
单方向的channel
select:多路复用
Goroutine泄漏
小结
并发与并行
在谈Go并发编程之前,我们需要对并发与并行做一下区分。
并发
并发是指有多个任务处于运行状态,但无法确定到底任务的运行顺序,比如某一时间,有一个双核CPU,但有10个任务(线程),这些任务可能随机被分配到相同或者不同的核心上去运行,但是其运行顺序是不确定的。
并行
并行是指多个任务在某一个时刻同时运行,比如某一个时刻,一个双核心的CPU,两个核心同时都有一个任务在运行,那么就是说这两个任务是并行的。
Goroutines
Goroutine是 Go语言的并发单元。
什么是Goroutine
Goroutine,中文称为协程,我们可以把 Goroutine看作是一个轻量级的线程,而从代码层面来看,Goroutine就是一个独立运行的函数或方法。
Goroutine的优势
- 与线程相比,创建一个Goroutine的开销要小得多,一个Goroutine初始化时只需要2KB,而一个线程则要2MB,所以Go程序可以大量创建Goroutine进行并发处理。
- 虽然协程初始化只有2KB,但却可以根据需求动态扩展。
- Goroutine可以通过Channel互相通讯,而线程只能通过共享内存互相通讯。
- Goroutine由Go调度器进行调度,而线程则依赖系统的调度。
启动Goroutine
要启动一个Goroutine非常简单,只要在函数或者方法前面加上 go关键字就可以了:
package main func Hello(){ fmt.Println("hello") } func main(){ go Hello() //匿名函数 go func(){ fmt.Println("My Goroutine") }() }
程序启动后, main函数单独运行在一个 Goroutine中,这个 Goroutine称作 Main Goroutine,其他用go关键字启动的Goroutine各自运行。
如果你在控制台运行上面的程序,会发现在控制台根据没有任何输出,这是为什么呢?
原因在于虽然所有的Goroutine是独自运行的,但如果 Man Gorouine终止的话,那么所有 Goroutine 都会退出执行。
上面的示例中,我们启动的 Goroutine还没运行,main函数就执行结束了,因此整个程序就退出了。
package main import "time" func Hello(){ fmt.Println("hello") } func main(){ go Hello() go func(){ fmt.Println("My Goroutine") }() time.Sleep(time.Second) }
上面的示例中,我们调用 time.Sleep()函数让 Main Goroutine休眠而不退出,这时候其他的Goroutine就可以在 Main Goroutine退出前执行。
关闭Goroutine
Go没有提供关闭Goroutine的机制,一般来说要让一个Goroutine停止有三种方式:
- random Goroutine执行完成退出或者 return退出
- main函数执行完成,所有Goroutine自然就会终止
- 直接终止整个程序的执行(程序崩溃或调用os.Exit()),类似第2种方式。
Channel
Go并发编程的思想是:不要用共享内存来通讯,而是用通讯来共享内存。而这种通讯机制就是Channel。
什么是Channel
Channel是 Goroutine之间的通信机制,可以把 Channel理解为 Goroutine之间的一条管道,就像水可以从一个管道的一端流向另一端一样,数据也可以通过 Channel从一个 Goroutine流向其他的一个 Goroutine,以实现 Goroutine之间的数据通讯。
创建Channel
创建 Channel类型的关键字是 chan,在 chan后面跟一个其他的数据类型,用于表示该 channel可发送什么类型的数据,比如一个可以发送整数的 Channel其定义是:
var ch chan int
Channel的默认值为nil,Channel必须实例化后才能使用,使用 make()函数实例化:
ch = make(chan int) ch1 := make(chan int)
Channel与map一样是引用数据类型,在调用make()函数后,该Channel变量引用一块底层数据结构,因此当把channel变量传递给函数时,调用者与被调用者引用的是同一块数据结构。
Channel操作
Channel支持发送与接收两种操作,无论是发送还是接收,都是用