零 基础
协程 一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程,协程的切换和创建完全是用户决定的
goroutine相对于线程:
1.Goroutine所需要的内存通常只有2kb,而线程则需要1Mb,内存消耗更少
2.由于线程创建时需要向操作系统申请资源,并且在销毁时将资源归还,因此它的创建和销毁的开销比较大。相比之下,goroutine的创建和销毁是由go语言在运行时自己管理的,因此开销更低。
3.切换开销更小线程的调度方式是抢占式的,如果一个线程的执行时间超过了分配给它的时间片,就会被其它可执行的线程抢占;而goroutine的调度是协同式的,它不会直接地与操作系统内核打交道
一 Mutex 互斥锁-Mutex是结构体
在同一时间段内有且只有一个goroutine持有锁,保证了在这段时间内只有一个goroutine访问共享资源,其他申请锁的goroutine将会被阻塞直到释放
常用函数 Lock() Unlock()
在Lock()和Unlock()之间的代码部分是临界区
var lock sync.Mutex //定义
go func(){
defer lock.Unlock()
lock.Lock()
//下面整套处理逻辑都是临界区
fmt.Prinln(time.now())
}
二 RWMutex 读写锁
R(Lock|UnLock) W(Lock|Unlock)
1 在同一个时间段内只能有一个goroutine获取到写锁
2 在同一个时间段内可以有多个goroutine获取到读锁
3 在同一时间段在只能存在写锁或者读锁(读锁和写锁互斥)
三 WaitGroup 并发等待组
var gp sync.WaitGroup
for i:=0;i<=5;i++{
gp.Add(1)
go func(i int) {
defer gp.Done()
fmt.Println("加锁",i)
time.Sleep(3)
fmt.Println("释放锁",i)
}(i)
}
fmt.Println("锁等待")
gp.Wait()
1 使用WaitGroup的goroutine会等待预设好数量的goroutine都提交执行结束后,才会继续往下执行代码
2 在goroutine调用WaitGroup之前我们需要保证WaitGroup中等待数据大于1
3 保证WaitGroup.Done()执行的次数与WaitGroup.Add()相同,过少会导致等待goroutine死锁,过多会导致painic