为什么 sleep 会禁用 go 中的互斥量?

why sleep will disable the mutex in go?

这是一个buyTicket程序,当ticket为0时,会显示“sell out”。我想知道为什么我不能在buyTicket函数中添加sleep,为什么ticket会是负值?

 func(t *Ticket) buyTicket() {
        if t.getSpareTicket() <= 0 {
            log.Print("sell out")
            return
        }
        t.mu.Lock()
        t.numTicket--
        time.Sleep(time.Microsecond)
        log.Printf("there are %d", t.numTicket)
        t.mu.Unlock()
    }
    
    func (t *Ticket) getSpareTicket() int{
        t.mu.Lock()
        defer t.mu.Unlock()
        return t.numTicket
    }
    
    
    func main() {
        buyer := &Ticket{}
        buyer.mu = sync.Mutex{}
        buyer.numTicket = 100
        for buyer.getSpareTicket() > 0 {
            //time.Sleep(time.Microsecond)
            go func() {
                log.Printf("number buy a ticket")
                buyer.buyTicket()
            }()
        }
    
        time.Sleep(time.Second * 2)
        //l := buyer.getSpareTicket()
        //fmt.Println(l)
    }

当我在函数buyTicket中添加time.sleep(time.microsecond)时,票会是负数,我想知道为什么会这样?

这是结果:

2020/11/15 15:36:00 there are 2
2020/11/15 15:36:00 there are 1
2020/11/15 15:36:00 there are 0
2020/11/15 15:36:00 there are -1
2020/11/15 15:36:00 there are -2
2020/11/15 15:36:00 there are -3
2020/11/15 15:36:00 there are -4
2020/11/15 15:36:00 there are -5

程序存在几个问题:

1- for 循环创建 goroutines 备用票数不为零。这将创建许多 goroutine,因为它们不会立即执行并减少票数

2- 在 buyTicket 中,您检查,然后购买。一个goroutine检查后,另一个goroutine可以进去做同样的事情,决定继续并买票。

解决办法是修复 buyTicket 在进入时锁定在退出时解锁,并在不调用 getSpareTicket 的情况下检查票数,因为 getSpareTicket 也锁定了相同的互斥锁,这将导致死锁。