3

go语言中有两种常见的锁机制,互斥锁,读写锁。

互斥锁

互斥锁比较暴力,当一个goroutine获得了互斥锁之后,其他的goroutine就只能够乖乖地等其解锁才能访问这个资源。
其中有两个公开的指针方法:

func (*Mutex) Lock

func (*Mutex) Unlock

这里借用一下之前文章《Go 临界资源的安全问题》中卖票的例子,加上互斥锁的代码如下:

package main

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

var ticket = 10 //the amount of the total ticket is 100

//创建一个锁🔐对象
var mutex sync.Mutex

//创建一个同步等待组对象
var wg sync.WaitGroup

func main() {
    wg.Add(4)
    //这里启动4个goroutine模拟4个售票口 同时售票
    go saleTickets("ticket window1")
    go saleTickets("ticket window2")
    go saleTickets("ticket window3")
    go saleTickets("ticket window4")
    //这里为了保证 主协程 最后执行完 先用sleep (当然,也可以用同步等待组、chanel实现)
    //time.Sleep(10 * time.Second) 用同步等待组waitgroup代替sleep
    wg.Wait()
}

func saleTickets(name string) {
    rand.Seed(time.Now().UnixNano())
    //票卖光,goroutine要结束的时候 需要把同步等待组的counter计数器-1
    defer wg.Done()
    for {
        //上锁🔐
        mutex.Lock()
        if ticket > 0 {
            time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
            fmt.Println(name, "saled: ", ticket)
            ticket--
        } else {
            //条件不满足 也要解锁🔓
            mutex.Unlock()
            fmt.Println(name, "sorry tickets are sold out")
            break
        }
        //🔓解锁
        mutex.Unlock()
    }
}

加上锁之后就不会出现卖出负票的情况了,结果如下:

ticket window4 saled:  10
ticket window4 saled:  9
ticket window2 saled:  8
ticket window3 saled:  7
ticket window1 saled:  6
ticket window4 saled:  5
ticket window2 saled:  4
ticket window3 saled:  3
ticket window1 saled:  2
ticket window4 saled:  1
ticket window2 sorry tickets are sold out
ticket window4 sorry tickets are sold out
ticket window1 sorry tickets are sold out
ticket window3 sorry tickets are sold out

需要注意的是:

  • 一定要记得解锁,否则就会发生异常or死锁等问题
  • 推荐使用defer语句来解锁

参考资料:bilibili


LiberHome
409 声望1.1k 粉丝

有问题 欢迎发邮件 📩 liberhome@163.com