2013-05-09 4 views
95

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

다음은 내가 가지고 있지만,보기 흉한 것 같습니다. 클리너/더 좋은 방법이 있습니까?

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

Playground

+2

(x)는 사용자의 예에 time.Duration를 이용해 주셔서 감사합니다. 내가 찾을 수있는 모든 예제는 하드 코드 된 int 있습니다 및 int (또는 부동) vars 사용할 때 불평합니다. –

+0

@MikeGraf 당신은't : = time.Tick (time.Duration (period) * time)을 할 수 있습니다.초)'기간이'int' 인 곳 – florianrosenberg

+0

이 해결책은 IMO가 꽤 좋은 것처럼 보입니다. 특히 단순히 외부 time.AfterFunc 대신 f()를 호출하면됩니다. 작업이 완료된 후 x 초 후에 일관된 간격으로 작업을 수행하려는 경우에 유용합니다. –

답변

160

기능 time.NewTicker 같은 대한주기적인 메시지를 전송하는 채널을 만들고, 그것을 막을 수있는 방법을 제공하는 방법

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

OP가 원하는 것에 따라 대답이 잘못되었습니다. 작업자가 사용하는 시간과 관계없이 OP가 정기 실행을 원할 경우 이동 루틴에서 '할 일'을 실행해야합니다. 그렇지 않으면 다음 작업자가 즉시 실행합니다 (5 초 이상 필요). – nemo

+2

IMO, 스케쥴러를 멈추고 싶을 때'닫는다 (종료) '해야한다. – Dustin

+0

종료 채널이 필요합니까? 우리가 증권 시세 표시기를 막을 수는 없습니까? –

17

.

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) 그것은이 (테스트되지 않은) 같은 것을 사용합니다.

+3

'time.Ticker'는'time.After'보다 낫습니다. 작업을 일정대로 유지하고 실행과 실행 사이의 임의 간격을 유지하는 것이 좋습니다. – Dustin

+0

더스틴이 옳습니다. 그냥 그만 해요. 멈추는 길에있는 애야. – Volker

+4

@Dustin 그리고 이것은 작업의 끝과 시작 사이에 고정 된 간격으로 작업을 수행하려는 경우 더 좋습니다. 어느 쪽도 최선이 아닙니다. 두 가지 사용 사례가 있습니다. – nos

2

이 질문에 대한 광범위한 대답은 종종 Occam에서 사용되며 JCSP을 통해 Java 커뮤니티에 제공되는 레거 벽돌 접근 방식을 고려할 수 있습니다. 이 아이디어에는 매우 좋은 presentation by Peter Welch가 있습니다.

Go는 Occam과 동일한 통신 순차 프로세스 기본을 사용하기 때문에이 플러그 앤 플레이 방식은 직접 Go로 변환됩니다.

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

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

각주 : Welch의 프레젠테이션에서 채널에 대한 Occam 구문을 사용합니다 ()!?이고 이것들은 Go에 직접 ch <-< -ch에 직접 해당합니다.

10

체크 아웃이 라이브러리 : 아래 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() 
14

당신이 틱은 (는 각 실행에 이전했다 한 시간에 따라) 변화에 대해 상관하지 않으면 당신은 할 채널을 사용하지 않으려면 기본 범위 기능을 사용할 수 있습니다.

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

Playground

관련 문제