почему sleep отключит мьютекс в go?

#go #mutex #sleep

#Вперед #мьютекс #сон

Вопрос:

Это программа покупки билетов, и когда билет равен 0, он отобразит «распродажа». Интересно, почему я не могу добавить sleep в функцию 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 := amp;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)
    }
  

когда я добавляю time.sleep(time.microsecond) в функцию buyTicket, билет будет отрицательным, я хочу знать, почему это происходит?

Это результат:

 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 проверяется задолго до запуска программ.

Ответ №1:

В программе есть несколько проблем:

1- Цикл for создает подпрограммы, в то время как количество запасных билетов не равно нулю. Это создаст много подпрограмм, поскольку они не будут выполняться немедленно и уменьшат количество билетов

2- В buyTicket вы проверяете, а затем покупаете. Другая подпрограмма может войти и сделать то же самое после того, как одна подпрограмма проверит, решит продолжить и купит билет.

Решение состоит в том, чтобы исправить buyTicket на блокировку при разблокировке входа при выходе и проверить количество билетов без вызова getSpareTicket, потому что getSpareTicket также блокирует тот же мьютекс, что приведет к взаимоблокировке.

Комментарии:

1. Спасибо за ответ. При удалении sleep в buyTicket проблем нет, интересно, почему добавление sleep может привести к тому, что билет может стать отрицательным. Кроме того, я попробовал ваше решение, оно зайдет в тупик.

2. Это не приведет к взаимоблокировке, если вы заблокируете один раз при вводе buyTicket, разблокируете при возврате и не вызываете getSpareTicket в buyTicket.

3. При режиме сна время, в течение которого мьютекс остается заблокированным, увеличивается, что повышает вероятность того, что несколько подпрограмм вызовут getSpareTicket(), увидят результат больше нуля и перейдут к покупке билета. Затем все они ждут, чтобы войти в критическую секцию, вызывая отрицательное количество билетов. Ваша программа работает без режима сна. Если вы запустите его достаточно много раз, в конечном итоге произойдет сбой.