Пример таймера с использованием timer.Reset () не работает, как описано - PullRequest
1 голос
/ 30 октября 2019

Я работал с примерами, пытаясь запустить мою первую "рутинную процедуру", и пока я запускал ее, она не будет работать, как предписано документацией go с функцией timer.Reset ().

В моем случае я считаю, что то, как я это делаю, просто прекрасно, потому что мне все равно, что находится в буфере чана, если что-нибудь происходит. Все, что предназначено для этого, - это вызвать case <-tmr.C:, если что-то произошло на case _, ok := <-watcher.Events:, и тогда все стихнет как минимум на одну секунду. Причина этого в том, что case _, ok := <-watcher.Events: может получить от одного до десятков событий за микросекунды друг от друга, и я забочусь только о том, как они все сделали, и все снова успокоилось.

Однако я обеспокоен тем, что делать этоТо, что в документации сказано, что вы «должны делать», не работает. Если бы я знал, что лучше, я бы сказал, что документация ошибочна, потому что она предполагает, что в буфере есть что-то, чего может не быть, но я не знаю, достаточно ли хорошо, чтобы быть уверенным в принятии такого решения, поэтому я надеюсь, что некоторые экспертытам можно просветить меня.

Ниже приведен код. Я не ставил это на игровую площадку, потому что мне нужно было бы провести некоторую очистку (удалить вызовы в другие части программы), и я не уверен, как бы я реагировал на изменения файловой системы, чтобы показать, как она работает.

Я четко указал в коде, какая альтернатива работает, а какая нет.

func (pm *PluginManager) LoadAndWatchPlugins() error {

  // DOING OTHER STUFF HERE

    fmt.Println(`m1`)

    done := make(chan interface{})
    terminated := make(chan interface{})

    go pm.watchDir(done, terminated, nil)
    fmt.Println(`m2.pre-10`)

    time.Sleep(10 * time.Second)

    fmt.Println(`m3-post-10`)

    go pm.cancelWatchDir(done)
    fmt.Println(`m4`)

    <-terminated
    fmt.Println(`m5`)

    os.Exit(0) // Temporary for testing

    return Err
}

func (pm *PluginManager) cancelWatchDir(done chan interface{}) {
    fmt.Println(`t1`)

    time.Sleep(5 * time.Second)
    fmt.Println()
    fmt.Println(`t2`)

    close(done)
}

func (pm *PluginManager) watchDir(done <-chan interface{}, terminated chan interface{}, strings <-chan string) {

  watcher, err := fsnotify.NewWatcher()
    if err != nil {
        Logger("watchDir::"+err.Error(), `plugins`, Error)
    }

    //err = watcher.Add(pm.pluginDir)
    err = watcher.Add(`/srv/plugins/`)
    if err != nil {
        Logger("watchDir::"+err.Error(), `plugins`, Error)
    }

    var tmr = time.NewTimer(time.Second)
    tmr.Stop()

    defer close(terminated)
    defer watcher.Close()
    defer tmr.Stop()
    for {
        select {
        case <-tmr.C:
            fmt.Println(`UPDATE FIRED`)
            tmr.Stop()

        case _, ok := <-watcher.Events:
            if !ok {
                return
            }

            fmt.Println(`Ticker: STOP`)
            /*
             *  START OF ALTERNATIVES
             *
             *  THIS IS BY EXAMPLE AND STATED THAT IT "MUST BE" AT:
             *      https://golang.org/pkg/time/#Timer.Reset
             *
             *  BUT DOESN'T WORK
             */
            if !tmr.Stop() {
                fmt.Println(`Ticker: CHAN DRAIN`)
                <-tmr.C // STOPS HERE AND GOES NO FURTHER
            }
            /*
             *  BUT IF I JUST DO THIS IT WORKS
             */
            tmr.Stop()
            /*
             *  END OF ALTERNATIVES
             */

            fmt.Println(`Ticker: RESET`)
            tmr.Reset(time.Second)

        case <-done:
            fmt.Println(`DONE TRIGGERED`)
            return
        }
    }
}

Ответы [ 3 ]

2 голосов
/ 30 октября 2019

Вы создаете таймер и немедленно его останавливаете:

var tmr = time.NewTimer(time.Second)
tmr.Stop()

Это не имеет никакого смысла, я предполагаю, что это просто "случайность" с вашей стороны.

НоДалее, внутри вашего цикла:

    case _, ok := <-watcher.Events:

Когда это происходит, вы утверждаете, что это не работает:

        if !tmr.Stop() {
            fmt.Println(`Ticker: CHAN DRAIN`)
            <-tmr.C // STOPS HERE AND GOES NO FURTHER
        }

Timer.Stop() документы, которые он возвращаетtrue, если этот вызов останавливает таймер, и false, если таймер уже был остановлен (или истек срок его действия). Но ваш таймер уже был остановлен, сразу после его создания, поэтому tmr.Stop() возвращает false правильно, поэтому вы заходите внутрь if и пытаетесь получить от tmr.C, но так как таймер был "долго" остановлен, ничегобудет отправлено на его канал, так что это блокирующая (навсегда) операция.

Если вы тот, кто явно останавливает таймер с помощью timer.Stop(), рекомендуемый «шаблон» для опустошения его канала нене имеет смысла и не работает для 2-го Timer.Stop() вызова.

0 голосов
/ 30 октября 2019

Кроме того, что icza сказал (qv), обратите внимание, что документация гласит:

Например, если программа еще не получила от tC:

if !t.Stop() {
        <-t.C
}

Это нельзя сделать одновременно с другими приемами от канала таймера.

Можно утверждать, что это не лучший пример, поскольку предполагается, что таймер работал в то время, когда вы звонили t.Stop. Но стоит упомянуть, что это плохая идея, если уже есть какая-то существующая программа, которая читает или может читать из t.C.

(документация Reset повторяет всеи в неправильном порядке, потому что Reset сортирует до Stop.)

По сути, вся область немного чревата . Нет хорошего общего ответа, потому что при возвращении с t.Stop назад на ваш звонок есть как минимум три возможных ситуации:

  • Никто не слушает канал, и таймер не установленканал сейчас. Это часто имеет место, если таймер уже был остановлен до вызова t.Stop. Если таймер уже был остановлен, t.Stop всегда возвращает false.
  • Никто не слушает канал, и сейчас в канале есть отметка таймера. Это всегда тот случай, когда таймер работал, но t.Stop не смог остановить его срабатывание. В этом случае t.Stop возвращает false. Это также тот случай, когда таймер работал , но срабатывал до , вы даже позвонили t.Stop, и поэтому остановились самостоятельно, так что t.Stop не смог его остановитьи вернул false.
  • Кто-то еще слушает канал.

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

Можно утверждать, что:

if !t.Stop() {
        select {
        case <-t.C:
        default:
        }
}

- лучший пример. Он делает одну неблокирующую попытку, которая использует тик таймера, если он присутствует, и ничего не делает, если тика таймера нет. Это работает независимо от того, был ли таймер на самом деле, когда вы вызывали t.Stop. В самом деле, это даже работает, если t.Stop возвращает true, хотя в этом случае t.Stop остановило таймер, поэтому таймеру никогда не удавалось поставить отметку таймера в канале. (Таким образом, если в канале есть данные, они обязательно должны быть перенесены из предыдущего сбоя при очистке канала. Если таких ошибок нет, попытка получения, в свою очередь, была излишней.)

Но, если кто-то еще - кто-то другой горутин - читает или может читать канал, вам вообще не следует этого делать. Невозможно узнать, кто (вы или они) получит любой таймер таймера, который может быть в канале, несмотря на вызов Stop.

Между тем, если вы не Если использовать таймер дальше, то относительно безопасно, просто оставить отметку таймера, если она есть, в канале. Он будет собираться мусором, когда сам канал будет собираться мусором. Конечно, имеет ли это смысл, зависит от того, что вы делаете с таймером, но в этих случаях достаточно просто вызвать t.Stop и проигнорировать его возвращаемое значение.

0 голосов
/ 30 октября 2019

вы не используете время. Время.

Попробуйте что-нибудь простое, как это

package main

import (
    "fmt"
    "log"
    "time"
)

func main() {
    timer := time.NewTimer(time.Second)
    watcher := make(chan bool)
    done := make(chan bool)
    evs := make(chan bool)
    go func() {
        trigger := false
        for {
            select {
            case <-timer.C:
                if trigger {
                    trigger = false
                    evs <- true
                }
                timer.Reset(time.Second)

            case _, ok := <-watcher:
                if !ok {
                    return
                }
                trigger = true

            case <-done:
                fmt.Println(`DONE TRIGGERED`)
                return
            }
        }
    }()
    go func() {
        for e := range evs {
            log.Println("e", e)
        }
    }()
    // simulate multiple events
    watcher <- true
    watcher <- true
    watcher <- true
    watcher <- true
    <-time.After(time.Second + time.Millisecond*100)
    watcher <- true
    watcher <- true
    watcher <- true
        <-time.After(time.Second + time.Millisecond*100)

    fmt.Println("Hello, playground")
}
...