Как правильно остановить таймер? - PullRequest
0 голосов
/ 08 мая 2018
var timer *time.Timer

func A() {
    timer.Stop() // cancel old timer
    go B() // new timer
}

func B() {
    timer = time.NewTimer(100 * time.Millisecond)
    select {
    case <- timer.C:
    // do something for timeout, like change state
    }
}

Функции А и В находятся в разных процедурах.

Скажи, что А в RPC-рутине. Когда приложение получает запрос RPC, оно отменяет старый таймер в B и запускает новый таймер в другой процедуре.

Док говорит:

Стоп не закрывает канал, чтобы предотвратить чтение из канала преуспевает неправильно.

Так, как сломать выбор в B, чтобы избежать утечки goroutine?

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

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

package main

import (
    "fmt"
    "time"
)

type CancellableTimer struct {
    cancel chan bool
}

func NewCancellableTimer() *CancellableTimer {
    return &CancellableTimer{
        cancel: make(chan bool),
    }
}

// internal wait goroutine wrapping time.After
func (c *CancellableTimer) wait(d time.Duration, ch chan bool) {
    select {
    case <-time.After(d):
        ch <- true
    case <-c.cancel:
        ch <- false
    }
}

// After mimics time.After but returns bool to signify whether we timed out or cancelled
func (c *CancellableTimer) After(d time.Duration) chan bool {
    ch := make(chan bool)
    go c.wait(d, ch)
    return ch
}

// Cancel makes all the waiters receive false
func (c *CancellableTimer) Cancel() {
    close(c.cancel)

}

// a goroutine waiting for cancellation
func B(t *CancellableTimer) {
    select {
    // timedOut will signify a timeout or cancellation
    case timedOut := <-t.After(time.Second):
        if timedOut {
            fmt.Println("Time out!")
        } else {
            fmt.Println("Cancelled!")
        }
    }
}

func main() {
    t := NewCancellableTimer()
    // Start 3 goroutines that wait for different timeouts on the same timer
    go B(t)
    go B(t)
    go B(t)

    // sleep a bit before cancelling
    time.Sleep(100 * time.Millisecond)

    // cancel the timer and all its waiters
    t.Cancel()

    // this is just to collect the output
    time.Sleep(time.Second)

}

Выход:

Cancelled!
Cancelled!
Cancelled!

ссылка на игровую площадку:

https://play.golang.org/p/z8OscJCXTvD

0 голосов
/ 08 мая 2018

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

import "time"

var timer *time.Timer
var canceled = make(chan struct{})

func A() {
    // cancel all current Bs
    select {
    case canceled <- struct{}{}:
    default:
    }   

    timer.Stop()

    go B()       // new timer
}

func B() {
    timer = time.NewTimer(100 * time.Millisecond)
    select {
    case <-timer.C:
        // do something for timeout, like change state
    case <-canceled:
        // timer aborted
    }
}

Обратите внимание, что все As и B соревнуются друг с другом за значение таймера. С помощью приведенного выше кода нет необходимости останавливать таймер A, поэтому вам не нужен глобальный таймер, исключающий гонку:

import "time"

var canceled = make(chan struct{})

func A() {
    // cancel all current Bs
    select {
    case canceled <- struct{}{}:
    default:
    }

    go B()
}

func B() {
    select {
    case <-time.After(100 * time.Millisecond):
        // do something for timeout, like change state
    case <-canceled:
        // aborted
    }
}
...