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

N.n*_*nnd -1 mutex sleep go

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

 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)
    }
Run Code Online (Sandbox Code Playgroud)

当我在函数 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
Run Code Online (Sandbox Code Playgroud)

Bur*_*dar 6

程序有几个问题:

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

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

解决方法是修复buyTicket在退出时锁定进入解锁,并且不调用getSpareTicket检查票数,因为getSpareTicket也锁定了同一个互斥锁,这会导致死锁。