#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(), увидят результат больше нуля и перейдут к покупке билета. Затем все они ждут, чтобы войти в критическую секцию, вызывая отрицательное количество билетов. Ваша программа работает без режима сна. Если вы запустите его достаточно много раз, в конечном итоге произойдет сбой.