天天看点

go语言基础 共享资源竞争 互斥锁sync.Mutex

共享资源竞争:

多个goroutine操作同一个共享数据,存在不安全。

i:=1
go func(){  //第一个Goroutine 我们叫他g1
   if i>0{ //①第一次g1执行到这里的时候,资源被g2抢走
     i--    
     fmt.Pringtln(i)  //输出i=0
   }
}// ③然后g1抢回资源 执行结束 此时i已经等于0
go func(){  //第二个Goroutine 我们叫他g2
   if i>0{ //②g2执行到这里的时候又被g1把资源抢走了
     i--  //④由于g1执行完毕 现在就剩下g2了 ,g2顺序执行,i--
     fmt.Pringtln(i)//⑤ 此时输出i已经变成-1了,按理论来说已经不满足条件了,但是由于判断已经完成了,所以g2输出的是-1
   }
}
           

一个共享数据比如i=1,被第一个Goroutien抢到了资源,条件是i>0,判断成立,下面执行i--,然后打印i,但是在i>0完之后又被第二个Goroutine抢到,同样执行判断i>0条件成立同样的 他也只是执行了条件判断,并没有执行i--和打印,这样就有两个满足条件的Goroutine了,第一个正常完成程序,正常输出i的值,但是第二个由于数据已经变了,所以输出的值是错误的。为了避免这种情况,我们就要给Goroutine加上互斥锁,在一个Goroutine访问数据时,别人是不能访问的,只有等访问完毕开锁之后,其他人才能来访问。

上锁:互斥锁(开,关)

        sync.Mutex

Lock(),UnLock()

我们来举一个卖票的例子,一共100张票,4个窗口卖,每卖一张,总票数-1,如果在不上锁的情况下,有可能最后会出现多卖出3张的情况,为了更好的展现卖出的过程,我再里面加了睡眠。

package main

import (
   "fmt"
   "sync"
   "math/rand"
   "time"
)

//全局变量
var ticket2 = 10 // 100张票
var wg3 sync.WaitGroup

var matex sync.Mutex // 创建锁头

func main() {
   /*
   4个goroutine,模拟4个售票口,4个子程序操作同一个共享数据。
    */
   wg3.Add(4)
   go saleticket2s("售票口1") // g1,100
   go saleticket2s("售票口2") // g2,100
   go saleticket2s("售票口3") //g3,100
   go saleticket2s("售票口4") //g4,100
   wg3.Wait()             // main要等待。。。
}

func saleticket2s(name string) {
   rand.Seed(time.Now().UnixNano())
   defer wg3.Done()
   //for i:=1;i<=100;i++{
   // fmt.Println(name,"售出:",i)
   //}

   for { //1, g1 ,g2,g3,g4
       //上锁, g2
      matex.Lock()
      if ticket2 > 0 { //g1,g3,g2,g4
         //睡眠
         time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
         // g1 ,g3, g2,g4
         fmt.Println(name, "售出:", ticket2)  // 1 , 0, -1 , -2
         ticket2--   //0 , -1 ,-2 , -3
      } else {
         matex.Unlock()//条件不满足,也要解锁,结束循环
         fmt.Println("售罄,没有票了。。")
         break
      }
      matex.Unlock()//解锁
   }
}