2014-05-10 21 views
11

Sto utilizzando goroutine/canali per controllare se l'elenco di URL è raggiungibile. Ecco il mio codice. Questo sembra sempre tornare vero. Perché il timeout non viene eseguito? L'obiettivo è di restituire false anche se uno degli URL non è raggiungibilegolang utilizzando timeout con i canali

import "fmt" 
import "time" 

func check(u string) bool { 
    time.Sleep(4 * time.Second) 
    return true 
} 

func IsReachable(urls []string) bool { 

    ch := make(chan bool, 1) 
    for _, url := range urls { 
     go func(u string) { 
      select { 
      case ch <- check(u): 
      case <-time.After(time.Second): 
       ch<-false 
      } 
     }(url) 
    } 
    return <-ch 
} 
func main() { 
    fmt.Println(IsReachable([]string{"url1"})) 
} 

risposta

10

check(u) dormirà nella corrente goroutine, ovvero quello in esecuzione func. L'istruzione select viene eseguita correttamente solo una volta che viene restituita e, a quel punto, entrambe le diramazioni sono eseguibili e il runtime può selezionare quello che preferisce.

È possibile risolverlo eseguendo check dentro ancora un altro goroutine:

package main 

import "fmt" 
import "time" 

func check(u string, checked chan<- bool) { 
    time.Sleep(4 * time.Second) 
    checked <- true 
} 

func IsReachable(urls []string) bool { 

    ch := make(chan bool, 1) 
    for _, url := range urls { 
     go func(u string) { 
      checked := make(chan bool) 
      go check(u, checked) 
      select { 
      case ret := <-checked: 
       ch <- ret 
      case <-time.After(1 * time.Second): 
       ch <- false 
      } 
     }(url) 
    } 
    return <-ch 
} 
func main() { 
    fmt.Println(IsReachable([]string{"url1"})) 
} 

Sembra che si desidera controllare la raggiungibilità di un insieme di URL, e ritornare vero se uno di loro è a disposizione. Se il timeout è lungo rispetto al tempo necessario per avviare una goroutine, è possibile semplificarlo avendo un solo timeout per tutti gli URL insieme. Ma abbiamo bisogno di fare in modo che il canale è abbastanza grande da contenere le risposte da tutti i controlli, o quelli che non "vincere" bloccherà per sempre:

package main 

import "fmt" 
import "time" 

func check(u string, ch chan<- bool) { 
    time.Sleep(4 * time.Second) 
    ch <- true 
} 

func IsReachable(urls []string) bool { 
    ch := make(chan bool, len(urls)) 
    for _, url := range urls { 
     go check(url, ch) 
    } 
    time.AfterFunc(time.Second, func() { ch <- false }) 
    return <-ch 
} 
func main() { 
    fmt.Println(IsReachable([]string{"url1", "url2"})) 
} 
+0

Grazie. In realtà, voglio tornare non raggiungibile se uno di essi non è raggiungibile. Quindi la funzione di controllo può scrivere "false" sul canale solo se non riesce a raggiungere l'url (e non scrive nulla se raggiunge l'url) entro il timeout e l'ora. AfterFunc può scrivere true dopo il timeout totale. – Kamal

3

La ragione per questo restituisce sempre vero è che si sta chiamando check(u) all'interno del vostro select dichiarazione. È necessario chiamarlo all'interno di una routine go e quindi utilizzare una selezione per attendere il risultato o il timeout.

Nel caso in cui si desideri verificare la raggiungibilità di più URL in parallelo, è necessario ristrutturare il codice.

Innanzitutto creare una funzione che controlla la raggiungibilità di un URL:

func IsReachable(url string) bool { 
    ch := make(chan bool, 1) 
    go func() { ch <- check(url) }() 
    select { 
    case reachable := <-ch: 
     return reachable 
    case <-time.After(time.Second): 
     // call timed out 
     return false 
    } 
} 

quindi chiamare questa funzione da un ciclo:

urls := []string{"url1", "url2", "url3"} 
for _, url := range urls { 
    go func() { fmt.Println(IsReachable(url)) }() 
} 

Play

0

cambiamento della linea

ch := make(chan bool, 1) 

a

ch := make(chan bool) 

Hai fatto aperto un asincrono (= non bloccante) del canale, ma è necessario un canale di blocco per farlo funzionare.