반복적 인 작업을 주기적으로 수행하는 방법이 있습니까?


148

Go에서 반복적 인 백그라운드 작업을 수행 할 수있는 방법이 있습니까? Timer.schedule(task, delay, period)Java 와 같은 것을 생각하고 있습니다. 나는 goroutine으로이 작업을 수행 할 수 있다는 것을 알고 Time.sleep()있지만 쉽게 멈추는 것을 원합니다.

여기에 내가 가진 것이 있지만 나에게는 못 생겼습니다. 더 깨끗하고 더 좋은 방법이 있습니까?

func oneWay() {
    var f func()
    var t *time.Timer

    f = func () {
        fmt.Println("doing stuff")
        t = time.AfterFunc(time.Duration(5) * time.Second, f)
    }

    t = time.AfterFunc(time.Duration(5) * time.Second, f)

    defer t.Stop()

    //simulate doing stuff
    time.Sleep(time.Minute)
}

3
예제에서 time.Duration (x)를 사용해 주셔서 감사합니다. 내가 찾을 수있는 모든 예제에는 하드 코딩 된 int가 있으며 int (또는 float) vars를 사용할 때 불평합니다.
Mike Graf

@MikeGraf 당신이 할 수있는 t := time.Tick(time.Duration(period) * time.Second)기간은이다int
florianrosenberg

이 솔루션은 꽤 좋은 것 같습니다. esp. u는 단순히 외부 시간 대신 f ()를 호출합니다. 작업이 완료된 후 x 초 동안 일관된 간격으로 작업을 수행하려는 경우에 적합합니다.
Luke W

답변:


240

이 기능 time.NewTicker은 주기적 메시지를 보내는 채널을 만들고 중지하는 방법을 제공합니다. 다음과 같이 사용하십시오 (예상치 않은).

ticker := time.NewTicker(5 * time.Second)
quit := make(chan struct{})
go func() {
    for {
       select {
        case <- ticker.C:
            // do stuff
        case <- quit:
            ticker.Stop()
            return
        }
    }
 }()

quit채널 을 닫으면 작업자를 중지 할 수 있습니다 close(quit)..


9
OP가 원하는 것에 따라 대답이 잘못되었습니다. OP가 작업자가 얼마나 많은 시간을 사용하든 관계없이 주기적으로 실행하려면 do stuff이동 루틴 을 실행해야합니다. 그렇지 않으면 다음 작업자가 즉시 실행됩니다 (5 초 이상 필요시).
nemo

2
IMO, close(quit)스케줄러를 중지하려는 경우 에만해야합니다 .
더스틴

3
시세 중지는 작동하지만 고 루틴은 가비지 수집되지 않습니다.
Paul Hankin

4
@SteveBrisk 문서를 참조하십시오 . 채널이 닫히면 읽기가 성공하고 원하지 않을 수 있습니다.
nemo

10
@ bk0에서 시간 채널은 "백업"되지 않습니다 (문서에 "느린 수신기를 보충하기 위해 간격을 조정하거나 틱을 떨어 뜨립니다"라고 표시됨). 주어진 코드는 당신이하는 말을 정확히 수행합니다 (최대 하나의 작업을 실행합니다). 작업 시간이 오래 걸리면 다음 호출이 단순히 지연됩니다. 뮤텍스가 필요하지 않습니다. 대신 새 작업이 모든 간격마다 시작되도록하려면 (이전이 완료되지 않은 경우에도) 그냥 사용하십시오 go func() { /*do stuff */ }().
Dave C

26

어때요?

package main

import (
    "fmt"
    "time"
)

func schedule(what func(), delay time.Duration) chan bool {
    stop := make(chan bool)

    go func() {
        for {
            what()
            select {
            case <-time.After(delay):
            case <-stop:
                return
            }
        }
    }()

    return stop
}

func main() {
    ping := func() { fmt.Println("#") }

    stop := schedule(ping, 5*time.Millisecond)
    time.Sleep(25 * time.Millisecond)
    stop <- true
    time.Sleep(25 * time.Millisecond)

    fmt.Println("Done")
}

운동장


3
A time.Tickertime.After작업을 일정대로 유지하는 것 보다 실행 간격을 임의로 지정하는 것 보다 낫습니다 .
더스틴

5
@Dustin 그리고 이것은 작업의 끝과 시작 사이에 고정 간격으로 작업을 수행하려는 경우에 좋습니다. 둘 다 최고는 아닙니다. 두 가지 사용 사례입니다.
nos

```// 이후 지속 시간이 경과 한 후 반환 된 채널에서 현재 시간을 보냅니다. // NewTimer (d) .C와 같습니다. // 타이머가 시작될 때까지 가비지 수집기에서 기본 타이머를 복구하지 않습니다. 효율성이 문제가 사용 NewTimer은```경우 어떻게 문에 대한 :If efficiency is a concern, use NewTimer

23

틱 시프 팅에 신경 쓰지 않고 (각 실행에 얼마나 오래 걸 렸는지에 따라) 채널을 사용하지 않으려는 경우 기본 범위 기능을 사용할 수 있습니다.

package main

import "fmt"
import "time"

func main() {
    go heartBeat()
    time.Sleep(time.Second * 5)
}

func heartBeat() {
    for range time.Tick(time.Second * 1) {
        fmt.Println("Foo")
    }
}

운동장


19

이 라이브러리를 확인하십시오 : https://github.com/robfig/cron

아래와 같은 예 :

c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly",      func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()

3

이 질문에 대한 더 넓은 대답은 Occam에서 자주 사용되며 JCSP 를 통해 Java 커뮤니티에 제공되는 레고 브릭 접근법을 고려할 수 있습니다 . 피터 웰치 (Peter Welch) 는이 아이디어에 대해 매우 훌륭한 발표를 합니다.

Go는 Occam과 동일한 커뮤니 케이 팅 순차 프로세스 기본 사항을 사용하므로이 플러그 앤 플레이 방식은 Go로 직접 변환됩니다.

따라서 반복적 인 작업을 설계 할 때 채널을 통해 이벤트 (예 : 메시지 또는 신호)를 교환하는 간단한 구성 요소 (고 루틴)의 데이터 흐름 네트워크로 시스템을 구축 할 수 있습니다.

이 접근 방식은 구성 적입니다. 각 소규모 구성 요소 그룹은 그 자체로 더 큰 구성 요소로 작동 할 수 있습니다. 복잡한 동시 시스템은 이해하기 쉬운 벽돌로 만들어지기 때문에 매우 강력 할 수 있습니다.

각주 : Welch의 프레젠테이션에서 그는 채널에 Occam 구문을 사용합니다 . 그리고 ? 이들은 Go의 ch <-<-ch 에 직접 대응됩니다 .


3

다음 코드를 사용합니다.

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println("\nToday:", now)

    after := now.Add(1 * time.Minute)
    fmt.Println("\nAdd 1 Minute:", after)

    for {
        fmt.Println("test")
        time.Sleep(10 * time.Second)

        now = time.Now()

        if now.After(after) {
            break
        }
    }

    fmt.Println("done")
}

더 간단하고 나에게 잘 작동합니다.


0

당신은 순간 시세 에서 그것을 중지하려는 경우

ticker := time.NewTicker(500 * time.Millisecond)
go func() {
    for range ticker.C {
        fmt.Println("Tick")
    }
}()
time.Sleep(1600 * time.Millisecond)
ticker.Stop()

을 멈추지 않으려면 :

tick := time.Tick(500 * time.Millisecond)
for range tick {
    fmt.Println("Tick")
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.