2013-06-03 18 views
5

Pisałem jakiś kod, który będzie jednocześnie odpytywania adresów URL co 30 minut:Zaplanowane zadania odpytywania Przejdź

func (obj * MyObj) Poll() { 
    for ;; { 
     for _, url := range obj.UrlList { 
      //Download the current contents of the URL and do something with it 
     } 
     time.Sleep(30 * time.Minute) 
} 

//Start the routine in another function 
go obj.Poll() 

Jak bym następnie dodać do obj.UrlList gdzie indziej w kodzie i upewnić się, że następnym razem, adresy URL są sondowane, że UrlList w Gromadzie ankiety również zostały zaktualizowane i jako takie będą również odpytywać nowy adres URL?

Rozumiem, że pamięć jest dzielona przez komunikowanie się, a nie odwrotnie w Go. Badałem kanały, ale nie jestem pewien, jak je zaimplementować w tym przykładzie.

+0

Może Cię zainteresować [ta rozmowa] (http://blog.golang.org/2013/05/advanced-go-concurrency-patterns.html). – nemo

Odpowiedz

8

Oto niesprawdzone, ale bezpieczny model okresowo pobierając kilka adresów z możliwością dynamicznie dodać nowe adresy URL do listy adresów URL bezpiecznie. Dla czytelnika powinno być oczywiste, co będzie konieczne, jeśli chcesz usunąć adres URL.

type harvester struct { 
    ticker *time.Ticker // periodic ticker 
    add chan string // new URL channel 
    urls []string  // current URLs 
} 

func newHarvester() *harvester { 
    rv := &harvester{ 
     ticker: time.NewTicker(time.Minute * 30), 
     add: make(chan string), 
    } 
    go rv.run() 
    return rv 
} 

func (h *harvester) run() { 
    for { 
     select { 
     case <-h.ticker.C: 
      // When the ticker fires, it's time to harvest 
      for _, u := range h.urls { 
       harvest(u) 
      } 
     case u := <-h.add: 
      // At any time (other than when we're harvesting), 
      // we can process a request to add a new URL 
      h.urls = append(h.urls, u) 
     } 
    } 
} 

func (h *harvester) AddURL(u string) { 
    // Adding a new URL is as simple as tossing it onto a channel. 
    h.add <- u 
} 
+0

To działało absolutnie świetnie, chociaż musisz zmienić h.ticker.C na <-h.ticker.C w przeciwnym razie pojawi się błąd "value not used". –

+0

Świetnie, cieszę się, że to zadziałało i było wystarczająco jasne, że można obejść błąd (który właśnie naprawiłem zgodnie z twoją specyfikacją). – Dustin

1
// Type with queue through a channel. 
type MyType struct { 
    queue chan []*net.URL 
} 

func (t *MyType) poll() { 
    for urls := range t.queue { 
     ... 
     time.Sleep(30 * time.Minute) 
    } 
} 

// Create instance with buffered queue. 
t := MyType{make(chan []*net.URL, 25)} 

go t.Poll() 
+0

Prawdopodobnie ankieta będzie nadal wywoływana, nawet jeśli t.queue nie zostanie zaktualizowane w okresie trzydziestominutowym? –

+2

W rzeczywistości, time.Sleep() jest niepotrzebne. Zakres nad kanałem będzie blokowany, dopóki nie będą dostępne wartości lub zakończy się po zamknięciu kanału. Zamiast zwykłego plasterka użyłbym prostego chan * net.URL, w przeciwnym razie potrzebowałbyś wewnętrznej pętli for (aby dotrzeć do plastra otrzymanego z kanału). – mna

+0

Tak, to szybki hack. ;) Śpi 30 minut pomiędzy każdą ankietą a blokami z pustą kolejką. Prawdziwe rozwiązanie użyłoby wybrania wewnętrznie i domyślnego, jeśli kolejka jest pusta. Uśpienie powinno odbywać się w tym oddziale, tak aby odbiór kanału działał tak długo, jak długo dostępne są dane, w przeciwnym razie cała pętla drzemie. – Mue

2

Jeśli trzeba odpytywać w regularnych odstępach czasu, to nie użytku time.Sleep ale time.Ticker zamiast (lub krewnego jak time.After) powinno. Powodem jest to, że sen to tylko sen i nie uwzględnia dryfu z powodu prawdziwej pracy, którą wykonałeś w swojej pętli. Odwrotnie, Ticker ma oddzielną goroutine i kanał, które razem są w stanie wysyłać ci regularne wydarzenia, a przez to powodować coś przydatnego do wykonania.

Oto przykład podobny do twojego. Wprowadzam losowy jitter, aby zilustrować korzyści z używania Ticker'a.

package main 

import (
    "fmt" 
    "time" 
    "math/rand" 
) 

func Poll() { 
    r := rand.New(rand.NewSource(99)) 
    c := time.Tick(10 * time.Second) 
    for _ = range c { 
     //Download the current contents of the URL and do something with it 
     fmt.Printf("Grab at %s\n", time.Now()) 
     // add a bit of jitter 
     jitter := time.Duration(r.Int31n(5000)) * time.Millisecond 
     time.Sleep(jitter) 
    } 
} 

func main() { 
    //go obj.Poll() 
    Poll() 
} 

Po uruchomieniu tego stwierdziłem, że pomimo jittera utrzymywał się w ścisłym 10-sekundowym cyklu.

Powiązane problemy