这是我参与「第三届青训营 -后端场」笔记创作活动的的第二篇笔记
GO并发编程
并发是什么?
并发是指立即处理多个任务的能力。在一个时间段可以处理多个事情,宏观上是一次多了多件事情,微观上,一次只能做一件事。在计算机中,只有一个CPU处理任务,但是CPU处理的很快,任务快速切换,用户看了就是 计算机同时做任务。
并行是什么?并行和并发有何区别?
并行是指同时处理多个任务。在一个微观的时刻,都可以做多件事情。一个计算机有多个CPU可以处理任务,同一时刻,可以把不同任务放在,不同CPU上运行。
Go 对并发的支持
Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发。
Go 协程是什么?
Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有大量的 Go 协程并发地运行。
Go 协程相比于线程的优势
- 相比线程而言,Go 协程的成本极低。堆栈大小只有若干 kb,并且可以根据应用的需求进行增减。而线程必须指定堆栈的大小,其堆栈是固定不变的。
- Go 协程会复用、数量更少的 OS 线程。即使程序有数以千计的 Go 协程,也可能只有一个线程。如果该线程中的某一 Go 协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个 OS 线程,并把其余 Go 协程都移动到这个新的 OS 线程。所有这一切都在运行时进行,作为程序员,我们没有直接面临这些复杂的细节,而是有一个简洁的 API 来处理并发。
通过go 关键字,启动一个协程
package main
import (
"fmt"
)
func hello() {
fmt.Println("Hello goroutine")
}
func main() {
go hello()
fmt.Println("main ")
}
协程之间的通信信道(Channel)
在GO语言的信道(Channel),可以理解为一个管道,管道两端的go协程,收发数据来通信。一段发送数据,一段来结束数据。 Go 协程使用信道(Channel)来进行通信。信道用于防止多个协程访问共享内存时发生竞态条件。
使用Channel通信
- 声明一个信道:
下面编写代码,声明一个信道。
package main
import "fmt"
func main() {
var a chan int //声明一个只可以传输int 的 信道
if a == nil {
fmt.Println("channel a is nil, going to define it")
a = make(chan int) //通过 make() 定义一个只可以传输 int的 信道
fmt.Printf("Type of a is %T", a)
}
}
如下所示,该语法通过信道发送和接收数据。
data := <- a // 读取信道 a
a <- data // 写入信道 a
信道旁的箭头方向指定了是发送数据还是接收数据。
死锁
使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。
我们还可以创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。
通过向 make 函数再传递一个表示容量的参数(指定缓冲的大小),可以创建缓冲信道。
ch := make(chan type, capacity)
通过使用capacity参数使得信道可以发送多条数据而不用等待,接受端立刻消费数据。