Storia della questione:
Fin dall'inizio, il linguaggio Go ha fornito il pacchetto time, all'interno del quale ci sono le funzioni principali per la gestione del tempo — time.Sleep e time.After. A differenza dei linguaggi con sonno di sistema (System.sleep), Go implementa timer asincroni tramite i suoi primitivi, il che è importante per il lavoro multithread.
Problema:
Spesso gli sviluppatori utilizzano in modo errato time.Sleep per implementare pause tra i compiti, il che è indesiderabile nei programmi concorrenti. Nella paradigmatica Go è più corretto costruire la gestione dell'attesa di eventi tramite canali e time.After per l'integrazione con select/canali.
Soluzione:
time.Sleep(d) blocca la goroutine corrente per d tempo, è un «sonno» diretto. time.After(d) restituisce un canale, sul quale dopo d apparirà un evento-tempo. Quest'ultima opzione è molto più flessibile in select per i canali, comoda per attese interrompibili e timeout.
Esempio di codice:
ch := make(chan struct{}) go func() { time.Sleep(2 * time.Second) ch <- struct{}{} }() select { case <-ch: fmt.Println("fatto") case <-time.After(1 * time.Second): fmt.Println("timeout") }
Caratteristiche chiave:
È possibile utilizzare time.Sleep per bloccare l'esecuzione dell'intero programma?
No, time.Sleep «addormenta» solo una goroutine, le altre continuano a lavorare.
Può time.After portare a perdite di memoria se il canale non viene letto?
Sì, il timer rimane attivo finché il valore non viene letto — in assenza di lettura, il garbage collector non elimina l'oggetto.
Esempio di codice:
func leak() { for { _ = time.After(time.Hour) } }
Come interrompere correttamente l'attesa di time.After se non si riceve un evento?
È meglio utilizzare time.Timer e fermare manualmente, se è necessario concludere l'attesa prima della scadenza:
t := time.NewTimer(time.Minute) if done { t.Stop() }
Una goroutine attende un segnale dal lavoro e nel caso di timeout fa così:
time.Sleep(10*time.Second) doSomething()
Pro:
Contro:
Il codice è costruito tramite select e time.After:
select { case <-workSignal: // Eseguire case <-time.After(10 * time.Second): // Fare per timeout }
Pro:
Contro: