进程/线程
进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的独立单位。
线程是进程的一个执行实体,是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{})