Go语言进阶 | 青训营

143 阅读5分钟

进程/线程

进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的独立单位。

线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

一个进程可以创建和撤销多个线程,同一个进程中的多个进程可以并发执行。

并发/并行

多线程程序在单核心的CPU上运行,称为并发;多线程程序在多核心的CPU上运行,go 1.8后默认设置为最大核数,不需自己设置。

协程/线程

协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。

线程:一个线程上可以跑多个协程,协程是轻量级的线程。

Goroutine

goroutine是一种非常轻量级的实现,可在单个进程里执行执行成千上万的并发任务,它是Go语言并发 设计的核心。

说到底 goroutine 其实就是线程,但是它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,而且Go语言内部也实现了 goroutine 之间的内存共享。

使用 go 关键字就可以创建 goroutine,将 go 声明放到一个需调用的函数之前,在相同地址空间调用运行这个函数,这样该函数执行时便会作为一个独立的并发线程,这种线程在Go语言中则被称为goroutine。

用法

//go 关键字放在方法调用前新建一个 goroutine 并执行方法体
go GetThingDone(param1, param2);
//新建一个匿名方法并执行
go func(param1, param2) {
}(val1, val2)
//直接新建一个 goroutine 并在 goroutine 中执行代码块
go {
//do someting...
}

并发安全Lock

一种同步访问共享资源的方式是使用互斥锁,互斥锁这个名字来自互斥的概念。互斥锁用于在代码上 创建一个临界区,保证同一时间只有一个 goroutine 可以执行这个临界代码。

sync.WaitGroup

sync.WaitGroup是一个结构体,现在让我们看看它的方法。

Add
//Add将delta(可能为负值)添加到WaitGroup计数器。
//如果计数器变为零,则释放在等待时阻塞的所有goroutine。
//如果计数器为负,则Add panics。

func (wg *WaitGroup) Add(delta int) {
//有兴趣可以自己去看源码
....
}

函数输入变量delta代表现在所在函数要调用的g程的个数,如果为零则释放所有阻塞的g程。

Done
// 完成将WaitGroup计数器递减一。
func (wg *WaitGroup) Done() {
wg.Add(-1)
}

等待组的计数器 -1,一般在被调用的g程中使用。

Wait
// 等待块,直到WaitGroup计数器为零。
func (wg *WaitGroup) Wait() {
...
}

wait其实就是一个阻塞当前调用wait方法的函数,比如在main函数调用就可以让main等待其他g程完 毕,如果没有等待main函数会直接退出,可能就做不到main中运行g程的结果了。

sync.Mutex(互斥锁)

定义

Mutex 是最简单的一种锁类型,同时也比较暴力,当一个 goroutine 获得了 Mutex 后,其他 goroutine就只能乖乖等到这个 goroutine 释放该 Mutex。

方法

任何一个 Lock() 均需要保证对应有 Unlock()调用与之对应,否则可能导致等待该锁的所有goroutine处于饥饿状态,甚至可能导致死锁。

sync.RWMutex(读写互斥锁)

定义

RWMutex 相对友好些,是经典的单写多读模型。在读锁占用的情况下,会阻止写,但不阻止读,也就 是多个 goroutine 可同时获取读锁(调用 RLock() 方法;而写锁(调用 Lock() 方法)会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该 goroutine 独占。从 RWMutex 的实现看, RWMutex 类型其实组合了 Mutex:

type RWMutex struct {
w Mutex
writerSem uint32
readerSem uint32
readerCount int32
readerWait int32
}
方法

任何一个RLock() 均需要保证对应有 RUnlock() 调用与之对应,否则可能导致等待该锁的所有 goroutine处于饥饿状态,甚至可能导致死锁。

Channel

如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制。一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的channel 一般写为 chan int。

channel是Go语言在语言级别提供的goroutine间的通信方式。我们可以使用channel在两个或多个 goroutine间通信。

通道的特性

Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。

通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

创建和声明

//声明
var 管道名 chan 类型
//创建
//必须使用 make 创建 channel
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})