Geschichte der Frage:
Von Anfang an hat die Sprache Go das Paket time bereitgestellt, innerhalb dessen die Hauptfunktionen zur Zeitverwaltung — time.Sleep und time.After. Im Gegensatz zu Sprachen mit Systemschlaf (System.sleep) implementiert Go asynchrone Timer über seine Primitiven, was für die Multithread-Arbeit wichtig ist.
Problem:
Entwickler verwenden oft time.Sleep falsch, um Pausen zwischen Aufgaben zu implementieren, was in konkurrierenden Programmen unerwünscht ist. In der Go-Paradigma ist es richtiger, die Verwaltung von Wait-Events über Kanäle und time.After für die Integration mit select/ Kanälen zu bauen.
Lösung:
time.Sleep(d) blockiert die aktuelle Goroutine für d Zeit, das ist ein direkter „Schlaf“. time.After(d) gibt einen Kanal zurück, auf den nach d ein Ereignis-Zeit auftaucht. Letztere Option ist viel flexibler in select über Kanäle, bequem für unterbrechbare Wartezeiten, Timeouts.
Beispielcode:
ch := make(chan struct{}) go func() { time.Sleep(2 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("done") case <-time.After(1 * time.Second): fmt.Println("timeout") }
Schlüsselfeatures:
Kann man time.Sleep verwenden, um die Ausführung des gesamten Programms zu blockieren?
Nein, time.Sleep „schläft“ nur eine Goroutine, die anderen arbeiten weiter.
Kann time.After zu einem Speicherleck führen, wenn der Kanal nicht gelesen wird?
Ja, der Timer bleibt bestehen, bis der Wert gelesen wird — wenn es kein Lesen gibt, wird der Garbage Collector das Objekt nicht löschen.
Beispielcode:
func leak() { for { _ = time.After(time.Hour) } }
Wie kann man das Warten mit time.After korrekt abbrechen, wenn kein Ereignis empfangen wird?
Es ist besser, time.Timer zu verwenden und manuell zu stoppen, wenn man das Warten vor Ablauf beenden muss:
t := time.NewTimer(time.Minute) if done { t.Stop() }
Die Goroutine wartet auf ein Signal von der Arbeit und macht in der Fall von Timeout folgendes:
time.Sleep(10*time.Second) doSomething()
Vorteile:
Nachteile:
Der Code wird über select und time.After aufgebaut:
select { case <-workSignal: // Ausführen case <-time.After(10 * time.Second): // Machen Sie es nach dem Timeout }
Vorteile:
Nachteile: