有没有一种方法可以定期执行重复的任务?

huangapple go评论80阅读模式
英文:

Is there a way to do repetitive tasks at intervals?

问题

有没有一种方法可以在Go中执行重复的后台任务?我想要的是类似于Java中的Timer.schedule(task, delay, period)。我知道可以使用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()

    //模拟执行任务
    time.Sleep(time.Minute)
}
英文:

Is there a way to do repetitive background tasks in Go? I'm thinking of something like Timer.schedule(task, delay, period) in Java. I know I can do this with a goroutine and Time.sleep(), but I'd like something that easily stopped.

Here's what I got, but looks ugly to me. Is there a cleaner/better way?

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

答案1

得分: 319

The function time.NewTicker makes a channel that sends a periodic message, and provides a way to stop it. Use it something like this (untested):

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

You can stop the worker by closing the quit channel: close(quit).

英文:

The function time.NewTicker makes a channel that sends a periodic message, and provides a way to stop it. Use it something like this (untested):

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

You can stop the worker by closing the quit channel: close(quit).

答案2

得分: 59

如果您不关心时间偏移(取决于之前每次执行所花费的时间长短)并且不想使用通道,可以使用原生的range函数。

例如:

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

英文:

If you do not care about tick shifting (depending on how long did it took previously on each execution) and you do not want to use channels, it's possible to use native range function.

i.e.

package main

import &quot;fmt&quot;
import &quot;time&quot;

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

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

Playground

答案3

得分: 32

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

}

英文:

How about something like

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

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

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

	return stop
}

func main() {
	ping := func() { fmt.Println(&quot;#&quot;) }

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

	fmt.Println(&quot;Done&quot;)
}

Playground
1: http://play.golang.org/p/UQ-ivZ_hZa

答案4

得分: 26

请查看这个库:https://github.com/robfig/cron

以下是示例代码:

c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("每小时的半点") })
c.AddFunc("@hourly",      func() { fmt.Println("每小时") })
c.AddFunc("@every 1h30m", func() { fmt.Println("每小时三十分钟") })
c.Start()
英文:

Check out this library: https://github.com/robfig/cron

Example as below:

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

答案5

得分: 10

如果你想在任何时刻停止它 ticker

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

如果你不想停止它 tick:

tick := time.Tick(500 * time.Millisecond)
for range tick {
	fmt.Println("Tick")
}
英文:

If you want to stop it in any moment ticker

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

If you do not want to stop it tick:

tick := time.Tick(500 * time.Millisecond)
for range tick {
	fmt.Println(&quot;Tick&quot;)
}

答案6

得分: 4

我使用以下代码:

package main

import (
    "fmt"
    "time"
)

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

    after := now.Add(1 * time.Minute)
    fmt.Println("\n加1分钟:", after)

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

        now = time.Now()

        if now.After(after) {
            break
        }
    }

    fmt.Println("完成")
}

对我来说,这更简单且运行良好。

英文:

I use the following code:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

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

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

	for {
		fmt.Println(&quot;test&quot;)
		time.Sleep(10 * time.Second)

		now = time.Now()

		if now.After(after) {
			break
		}
	}

	fmt.Println(&quot;done&quot;)
}

It is more simple and works fine to me.

答案7

得分: 3

对于这个问题的更广泛的回答可能考虑到Occam中经常使用的乐高积木方法,并通过JCSP提供给Java社区。关于这个想法,有一个非常好的Peter Welch的演讲

这种即插即用的方法直接转化为Go语言,因为Go使用与Occam相同的通信顺序进程基础知识。

因此,当涉及到设计重复任务时,您可以将系统构建为由简单组件(如goroutine)组成的数据流网络,这些组件通过通道交换事件(即消息或信号)。

这种方法是组合的:每个小组件组可以自身作为一个更大的组件行为,无限循环。这非常强大,因为复杂的并发系统是由易于理解的积木构建而成。

脚注:在Welch的演讲中,他使用Occam语法表示通道,即*!?,这直接对应于Go语言中的ch<-<-ch**。*

英文:

A broader answer to this question might consider the Lego brick approach often used in Occam, and offered to the Java community via JCSP. There is a very good presentation by Peter Welch on this idea.

This plug-and-play approach translates directly to Go, because Go uses the same Communicating Sequential Process fundamentals as does Occam.

So, when it comes to designing repetitive tasks, you can build your system as a dataflow network of simple components (as goroutines) that exchange events (i.e. messages or signals) via channels.

This approach is compositional: each group of small components can itself behave as a larger component, ad infinitum. This can be very powerful because complex concurrent systems are made from easy to understand bricks.

Footnote: in Welch's presentation, he uses the Occam syntax for channels, which is ! and ? and these directly correspond to ch<- and <-ch in Go.

答案8

得分: 3

你可以像这样简单地使用gocron包:

package main

import (
    "fmt"
    "time"
    // go get github.com/go-co-op/gocron
    "github.com/go-co-op/gocron"
)

func main() {
    s := gocron.NewScheduler(time.UTC)
    s.Every(3).Seconds().Do(func() { fmt.Println("Every 3 seconds") })
    // 你可以以两种不同的方式开始运行调度器:
    // 异步地启动调度器
    s.StartAsync()
    // 启动调度器并阻塞当前执行路径
    // s.StartBlocking()
}

gocron: https://github.com/go-co-op/gocron

英文:

You can simply use gocron package like this:

package main

import (
 &quot;fmt&quot;
 &quot;time&quot;
 // go get github.com/go-co-op/gocron
 &quot;github.com/go-co-op/gocron&quot;
)

func main() {
 s := gocron.NewScheduler(time.UTC)
 s.Every(3).Seconds().Do(func() { fmt.Println(&quot;Every 3 seconds&quot;) })
 // you can start running the scheduler in two different ways:
 // starts the scheduler asynchronously
 s.StartAsync()
 // starts the scheduler and blocks current execution path
 // s.StartBlocking()
}

gocron: https://github.com/go-co-op/gocron

huangapple
  • 本文由 发表于 2013年5月10日 00:13:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/16466320.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定