go基础系列~并发协程

零 基础
协程 一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程,协程的切换和创建完全是用户决定的

很多时候 Mutex 并不是单独使用的,而是嵌套在 Struct 中使用,作为结构体的一部分,如果嵌入的 struct 有多个字段,我们一般会把 Mutex 放在要控制的字段上面,然后使用空格把字段分隔开来。

甚至可以把获取锁、释放锁、计数加一的逻辑封装成一个方法。

type Person struct {
mux sync.Mutex|WaitGroup
}
func (p *Peron) test(){
    def p.mux.UnLock()
p.mux.Lock()
}
func main() {
p1:=Person{mux: sync.Mutex{}} #初始化赋值

}

看到这里我们就能发现, 当 struct 嵌套了 Mutex, 如果以值传递的方式使用时, 有可能造成程序死锁, 有可能需要互斥的变量并不能达到互斥.

所以不管是单独使用 不能复制 类型的变量, 还是嵌套在 struct 里面都不能值传递的方式使用


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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章