2013-04-29 17 views
6

Sto imparando Vai ora usando e uno dei miei primi progetti è un semplice script ping. In sostanza, voglio eseguire il ping di un gruppo di URL e, su risposta di ognuno, attendere XXX numero di secondi, quindi eseguire nuovamente il ping. Ecco il codice abbreviato:Il lancio di goroutine all'interno delle goroutine è accettabile?

func main() { 
    // read our text file of urls 
    f, err := ioutil.ReadFile(urlFile) 
    if err != nil { 
     log.Print(err) 
    } 

    urlStrings := []string{} 
    urlStrings = strings.Split(string(f), "\n") 

    for _, v := range urlStrings { 
     go ping(v) 
    } 

    // output logs to the terminal 
    // channel is global 
    for i := range c { 
     fmt.Println(i) 
    } 
} 

func ping(url string) { 
    // for our lag timer 
    start := time.Now() 

    // make our request 
    _, err := http.Get(url) 

    if err != nil { 
     msg := url + " Error:" + err.Error() 

     fmt.Println(msg) 

     c <- msg 
     reportError(msg) 
    } else { 
     lag := time.Since(start) 
     var msg string 

     // running slow 
     if lag > lagThreshold*time.Second { 
      msg = url + " lag: " + lag.String() 
      reportError(msg) 
     } 

     msg = url + ", lag: " + lag.String() 
     c <- msg 
    } 

    time.Sleep(pingInterval * time.Second) 
    go ping(url) // is this acceptable? 
} 

Su mia richiesta Get ero in precedenza chiamando rinviare res.Body.Close(), ma che è stato panicing dopo l'applicazione ha funzionato per un po '. Supponevo che il differimento non potesse chiamare il Close() sulla risposta finché la goroutine non fosse stata raccolta dalla spazzatura e la res non esistesse più.

Questo mi ha fatto pensare se chiamare una goroutine all'interno di una goroutine fosse una buona pratica o se io facessi in modo che la funzione non uscisse mai, e quindi un rinvio sarebbe stato chiamato solo una volta che la goroutine era stata raccolta.

+0

Non ci dovrebbero essere problemi a generare goroutine da altre goroutine, ma c'è qualche ragione per non usare solo un ciclo in questo caso? –

+0

@JamesHenstridge I Non sto facendo questo in un loop procedurale quindi non devo aspettare che ogni richiesta ritorni prima di chiamare il prossimo. Sto cercando di usare la concorrenza in modo che ogni ciclo di ping sia indipendente in base al proprio tempo di latenza. – ARolek

+0

Mi riferivo alla parte in cui la goroutine 'ping()' genera un'altra goroutine appena prima di uscire. Se metti un ciclo all'interno di 'ping()', otterrai lo stesso effetto. –

risposta

11

Questo va bene. È perfettamente accettabile chiamare una goroutine da un'altra goroutine. La goroutine chiamante uscirà comunque e la nuova goroutine andrà avanti in modo allegro.

7

L'estensione di una nuova goroutine dall'interno di una goroutine è di per sé perfettamente soddisfacente.

Ma dubito che questa sia la soluzione più semplice e più pulita al problema. Immagino che la tua prima versione abbia fatto la cosa ovvia e ping ogni URL in un ciclo infinito. E questo morde rimanda: le chiamate rimandate vengono eseguite una volta che la funzione restituisce. (Questo non ha nulla a che fare con una goroutine di "raccolta dei rifiuti, in realtà le goroutine terminano, non vengono raccolti). In un ciclo infinito non si ritorna mai, si accumulano solo chiamate posticipate che non vengono mai eseguite. res.Body e si esaurisce la memoria/qualunque cosa e vedere il panico.

facendo defer res.Body.Close è un bel linguaggio, ma non all'interno di un ciclo infinito.

vorrei provare la vostra prima versione e direttamente eseguire res.Body .Chiudi sul percorso di errore nil

+0

se non chiamo res.Body.Close() sarà anche il composto di utilizzo della memoria, o la res verrà scartata? – ARolek

+0

È ** necessario ** chiamare res.Body.Close. È documentato per qualche motivo. (Il tuo codice potrebbe fuoriuscire in altro modo.) – Volker

+0

e se non lo faccio a un var come sto facendo sopra? _, err: = http.Get (url) si verificherà questa perdita? – ARolek

Problemi correlati