为什么睡眠会禁用go中的互斥?

为什么睡眠会禁用go中的互斥?,go,mutex,sleep,Go,Mutex,Sleep,这是一个买票程序,当票子为0时,它将显示“卖完”。我想知道为什么我不能在buyTicket功能中添加睡眠,为什么ticket会是负数 func(t *Ticket) buyTicket() { if t.getSpareTicket() <= 0 { log.Print("sell out") return } t.mu.Lock() t.numTicket

这是一个买票程序,当票子为0时,它将显示“卖完”。我想知道为什么我不能在buyTicket功能中添加睡眠,为什么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)
    }

该计划存在几个问题:

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

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


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

发生这种情况是因为在goroutines开始运行之前已检查for条件。谢谢您的回答。当我在buyTicket中删除sleep时没有问题,我想知道为什么添加sleep可能会导致ticket变为负值。另外,我尝试了您的解决方案,它会死锁。如果您在输入buyTicket时锁定一次,返回时解锁,并且不在buyTicket中调用getSpareTicket,则不会死锁。使用睡眠时,互斥锁保持锁定的时间更长,这使得更有可能有多个GoRoutine调用getSpareTicket(),看到结果大于零,继续买票。然后,他们都等待进入关键区域,导致票数为负数。你的程序有一个没有睡眠的比赛。如果您运行它足够多次,它最终将失败。
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