Поведение Ticker Stop в Golang

Если я перемещаюсь по каналу тикера и вызываю stop(), канал останавливается, но не закрывается.

В этом примере:

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)
}

Бег производит:

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

Так что горутина никогда не выходит. Есть ли лучший способ справиться с этим случаем? Должен ли я заботиться о том, чтобы горутина никогда не завершалась?


person whatupdave    schedule 22.07.2013    source источник
comment
Вы получаете утечку памяти, если подпрограмма go не завершается. Вызовите close(ticker.C), чтобы освободить подпрограмму Go.   -  person fuz    schedule 23.07.2013
comment
Невозможно закрыть: невозможно закрыть канал только для приема   -  person whatupdave    schedule 23.07.2013
comment
Документы golang должны действительно расширить это, но их пример с тикером показывает, как использовать готовый канал, чтобы исключить стандартную утечку: golang.org/pkg/time/#example_NewTicker   -  person colm.anseo    schedule 27.03.2018


Ответы (4)


Подайте сигнал «готово» на втором канале и выберите в своей горутине между тикером и каналом «готово».

В зависимости от того, что вы действительно хотите сделать, может существовать лучшее решение, но это трудно сказать по сокращенному демонстрационному коду.

person Volker    schedule 22.07.2013

Использовал второй канал, как предложил Волкер. Вот с чем я столкнулся:

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)
}
person whatupdave    schedule 22.07.2013
comment
Если ваша работа займет 4 секунды, вы заблокируете свою горутину, и она застрянет, пытаясь записать на канал, который является единственным читателем. Вы действительно просто хотите переменную состояния на for{} - и не посылайте на канал остановки, просто закройте его. - person Dustin; 23.07.2013
comment
также nil каналы являются отличным способом передачи сигналов после операции выхода (close(quit)) любому другому чтению канала: см. отсутствие каналов по сравнению с состоянием переменная. - person colm.anseo; 27.03.2018

вы можете сделать так.

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)
}
person ahoLic    schedule 11.08.2015

Если вам нужно сэкономить больше места, используйте каналы пустых структур - struct{}, которые не требуют памяти. И, как упоминалось выше, не отправляйте в него что-либо - просто закройте, что на самом деле отправит нулевое значение.

person free2use    schedule 23.10.2014
comment
не уверен, почему за это проголосовали: quit := make(chan struct{}) — отличная техника и четкое указание на то, для чего используется канал, то есть для подачи сигнала об отключении через close(quit) - person colm.anseo; 27.03.2018