Achtergrond van de vraag:
Vanaf het begin heeft de taal Go het time-pakket aangeboden, waarin de belangrijkste functies voor tijdbeheer zijn ondergebracht — time.Sleep en time.After. In tegenstelling tot talen met systeemslaap (System.sleep), implementeert Go asynchrone timers via zijn eigen primitieve functies, wat belangrijk is voor multithreaded werk.
Probleem:
Ontwikkelaars gebruiken vaak time.Sleep verkeerd voor het implementeren van pauzes tussen taken, wat ongewenst is in concurrerende programma's. In de Go-paradigma is het beter om het wachten op gebeurtenissen te beheren via kanalen en time.After voor integratie met select/kanalen.
Oplossing:
time.Sleep(d) blokkeert de huidige goroutine voor d tijd, dit is een directe "slaap". time.After(d) retourneert een kanaal waarop na d een tijd-gebeurtenis verschijnt. De laatste optie is veel flexibeler in select over kanalen, en handig voor onderbroken wachttijden en time-outs.
Voorbeeldcode:
ch := make(chan struct{}) go func() { time.Sleep(2 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("gedaan") case <-time.After(1 * time.Second): fmt.Println("time-out") }
Belangrijke kenmerken:
Kun je time.Sleep gebruiken om de uitvoering van het hele programma te blokkeren?
Nee, time.Sleep "laat" alleen één goroutine in slaap vallen, de andere gaan door met werken.
Kan time.After leiden tot geheugenlekken als het kanaal niet wordt gelezen?
Ja, de timer blijft hangen totdat de waarde is gelezen — als er geenLeesbewerking is, zal de garbage collector het object niet verwijderen.
Voorbeeldcode:
func leak() { for { _ = time.After(time.Hour) } }
Hoe kun je het wachten op time.After correct annuleren bij het niet ontvangen van een gebeurtenis?
Het is beter om time.Timer te gebruiken en handmatig te stoppen als je het wachten voor de deadline wilt beëindigen:
t := time.NewTimer(time.Minute) if done { t.Stop() }
Een goroutine wacht op een signaal van werk, en voor het geval van een time-out doet het dit:
time.Sleep(10*time.Second) doSomething()
Voordelen:
Nadelen:
De code is opgebouwd via select en time.After:
select { case <-workSignal: // Uitvoeren case <-time.After(10 * time.Second): // Doen naar de time-out }
Voordelen:
Nadelen: