2013-07-22 12 views
10

Jeśli przekraczam kanał ticker i stop(), kanał jest zatrzymywany, ale nie zamknięty.Zachowanie Ticker Stop w Golang

w poniższym przykładzie:

package main 

import (
    "time" 
    "log" 
) 

func main() { 
    ticker := time.NewTicker(1 * time.Second) 
    go func(){ 
     for _ = range ticker.C { 
      log.Println("tick") 
     } 
     log.Println("stopped") 
    }() 
    time.Sleep(3 * time.Second) 
    log.Println("stopping ticker") 
    ticker.Stop() 
    time.Sleep(3 * time.Second) 
} 

Bieg produkuje:

2013/07/22 14:26:53 tick 
2013/07/22 14:26:54 tick 
2013/07/22 14:26:55 tick 
2013/07/22 14:26:55 stopping ticker 

Więc nigdy, że goroutine wychodzi. Czy istnieje lepszy sposób na obsługę tej sprawy? Czy powinienem się przejmować, że goroutine nigdy się nie kończy?

+1

Występuje przeciek pamięci, jeśli procedura rutowania nie zostanie zakończona. Zadzwoń zamknij (ticker.C), aby zwolnić procedurę Go. – fuz

+3

Nie można zamknąć: "nie można zamknąć kanału tylko do odbioru" – whatupdave

Odpowiedz

10

Sygnał "wykonano" na drugim kanale i wybierz w swojej goroutine między kanałem a zakończonym kanałem.

W zależności od tego, co naprawdę chcesz zrobić, lepsze rozwiązanie może istnieć, ale trudno o nim powiedzieć po zmniejszeniu kodu demonstracyjnego.

11

Użyłem drugiego kanału, jak zasugerował Volker. Właśnie to zakończyło się:

package main 

import (
    "log" 
    "time" 
) 

// Run the function every tick 
// Return false from the func to stop the ticker 
func Every(duration time.Duration, work func(time.Time) bool) chan bool { 
    ticker := time.NewTicker(duration) 
    stop := make(chan bool, 1) 

    go func() { 
     defer log.Println("ticker stopped") 
     for { 
      select { 
      case time := <-ticker.C: 
       if !work(time) { 
        stop <- true 
       } 
      case <-stop: 
       return 
      } 
     } 
    }() 

    return stop 
} 

func main() { 
    stop := Every(1*time.Second, func(time.Time) bool { 
     log.Println("tick") 
     return true 
    }) 

    time.Sleep(3 * time.Second) 
    log.Println("stopping ticker") 
    stop <- true 
    time.Sleep(3 * time.Second) 
} 
+4

Jeśli twoja praca zajmuje 4 sekundy, zablokujesz swoją gorsupinę i utknie ona, próbując napisać do kanału, że jest to jedyny czytnik. Po prostu potrzebujesz zmiennej stanu na 'for {}' - i nie wysyłaj na kanał stop, po prostu zamknij go. – Dustin

-1

Jeśli potrzebujesz więcej miejsca, użyj kanałów pustych struktur - struct {}, które nie wymagają pamięci. I jak wspomniano powyżej, nie wysyłaj w nim czegoś - po prostu blisko, co faktycznie wysyła zerową wartość.

0

możesz to zrobić.

package main 

import (
    "fmt" 
    "time" 
) 

func startTicker(f func()) chan bool { 
    done := make(chan bool, 1) 
    go func() { 
     ticker := time.NewTicker(time.Second * 1) 
     defer ticker.Stop() 
     for { 
      select { 
      case <-ticker.C: 
       f() 
      case <-done: 
       fmt.Println("done") 
       return 
      } 
     } 
    }() 
    return done 
} 

func main() { 
    done := startTicker(func() { 
     fmt.Println("tick...") 
    }) 
    time.Sleep(5 * time.Second) 
    close(done) 
    time.Sleep(5 * time.Second) 
}