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
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号