Go如何通过间隔调用Ticker?

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

How does Go invoke Ticker by interval?

问题

由于我不是经验丰富的Go开发人员,我不理解与Ticker一起工作的方式。我有以下场景:

一个运行在特定端口8080上的Go Web服务,它从其他应用程序获取数据并处理数据。到目前为止一切顺利,但是在这个Web服务中我有另一个sendData函数,它会循环遍历一些文件并将它们发送到另一个外部服务。我想每隔1分钟调用一次sendData()函数。以下是没有使用Ticker的主函数的样子:

func main() {
	http.HandleFunc("/data", headers)          //line 1
	log.Printf("Ready for data ...%s\n", 8080) //line 2
	http.ListenAndServe(":8080", nil)          //line 3
}

如果我在line 2之后添加Ticker,它会无限循环。
如果我在line 3之后添加,程序不会调用Ticker。
有什么办法可以处理这个问题吗?

Ticker部分的代码如下:

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

以及来自schedule函数的代码:

func schedule(f func(), interval time.Duration, done <-chan bool) *time.Ticker {
	ticker := time.NewTicker(interval)
	go func() {
		for {
			select {
			case <-ticker.C:
				f()
			case <-done:
				return
			}
		}
	}()
	return ticker

所以基本上我想每隔一分钟或一小时等发送一次数据。有人能解释一下Ticker的内部工作原理吗?

英文:

Since I am not experinced Go developer, I didn't understand way of working with Ticker. I have following scenario:

A go web service running on specific port 8080, it is getting data from another applications and processing the data. So far so good, but I have a another sendData function in this web service which loop through the some files and send them to another extern service. I am trying to call the sendData() function every 1 minute. Here is how main function looks without Tickers:

func main() {

	http.HandleFunc(&quot;/data&quot;, headers)          //line 1
	log.Printf(&quot;Ready for data ...%s\n&quot;, 8080) //line 2
	http.ListenAndServe(&quot;:8080&quot;, nil)          //line 3
}

If I add the Ticker after line 2 it's keeping loop infinitively.
If I add after line 3, the programm is not invoking the Ticker.
Any idea how to handle this?

The Ticker part

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

and the schedule from

func schedule(f func(), interval time.Duration, done &lt;-chan bool) *time.Ticker {
	ticker := time.NewTicker(interval)
	go func() {
		for {
			select {
			case &lt;-ticker.C:
				f()
			case &lt;-done:
				return
			}
		}
	}()
	return ticker

So basically I want to sendData evert minute or hour etc. Could someone explain how internally Ticker works?

答案1

得分: 0

http.ListenAndServe(":8080", nil) 运行一个无限的 for 循环来监听传入的连接,这就是为什么如果你在之后调用它,计时器不会被调用。

然后在这里

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

在 60 秒后,你在 schedule() 函数内退出了循环,所以你的计时器只会运行一次或者根本不会运行(取决于 done 通道是在计时器滴答之前还是之后接收到值,因为它们是并发的,我们无法确定它们的顺序)。

所以你想要的是以下代码:

func main() {
    http.HandleFunc("/data", headers)

    ticker := time.NewTicker(time.Minute)
    go schedule(ticker)

    log.Printf("Ready for data ...%s\n", 8080)
    http.ListenAndServe(":8080", nil)
}

func schedule(ticker *time.Ticker) {
    for {
        // 这里会阻塞,直到接收到一个值,计时器每一分钟(或指定的间隔)会发送一个值
        <-ticker.C
        fmt.Println("Tick")
    }
}

你可能已经注意到,一旦服务器连接中断,程序将终止,所以没有必要使用 done 通道来退出循环。

在这里尝试一下

英文:

http.ListenAndServe(&quot;:8080&quot;, nil) runs an infinite for loop listening for inbound connections, that's why the ticker is not invoked if you call it afterwards.

And then here

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

you're exiting the loop inside schedule() after 60 seconds, so your ticker will run only once or won't run at all (depending on whether the done channel receives the value before or later that the ticker ticks, as they are concurrent we cannot determine their order)


So what you want is the following

func main() {
    http.HandleFunc(&quot;/data&quot;, headers)

    ticker := time.NewTicker(time.Minute)
    go schedule(ticker)

    log.Printf(&quot;Ready for data ...%s\n&quot;, 8080)
    http.ListenAndServe(&quot;:8080&quot;, nil)
}

func schedule(ticker *time.Ticker) {
    for {
        // This blocks until a value is received, the ticker
        // sends a value to it every one minute (or the interval specified)
        &lt;-ticker.C
        fmt.Println(&quot;Tick&quot;)
    }
}

As you may have noticed, once the server connection is interrupted the program will terminate so there's no point on having a done channel to exit the loop.

Try it here

答案2

得分: -2

你正在走在正确的轨道上 - 你只需要将ticker声明包装在一个自执行函数中,然后作为一个goroutine运行。ListenAndServeSchedule都是阻塞任务,所以它们需要在不同的goroutine中运行。幸运的是,Go语言使这一点非常简单。

注意 - 这个示例代码旨在尽可能接近你的示例。我建议将ticker的声明与schedule函数分开。

func main() {
    http.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {}) //line 1

    var done chan bool

    go func() {
        ticker := schedule(func() { fmt.Println("Tick") }, time.Second, done)
        time.Sleep(60 * time.Second)
        close(done)
        ticker.Stop()
    }()

    fmt.Printf("Ready for data ...%v\n", 8080) //line 2
    http.ListenAndServe(":8080", nil)          //line 3
}
英文:

You are on the right track - you just need to wrap the ticker declaration in a self executing function and then run it as a goroutine. ListenAndServe and Schedule are both blocking tasks, so they need to run on separate go routines. Luckily go makes this really simple to achieve.

Note - this sample code is meant to stay as close to your example as possible. I would recommend separating the declaration of the ticker from the schedule func.

func main() {

	http.HandleFunc(&quot;/data&quot;, func(w http.ResponseWriter, req *http.Request) {}) //line 1

	var done chan bool

    go func() {
	    ticker := schedule(func() { fmt.Println(&quot;Tick&quot;) }, time.Second, done)
	    time.Sleep(60 * time.Second)
	    close(done)
	    ticker.Stop()
    }()

	fmt.Printf(&quot;Ready for data ...%v\n&quot;, 8080) //line 2
    http.ListenAndServe(&quot;:8080&quot;, nil)          //line 3
}

huangapple
  • 本文由 发表于 2022年10月25日 21:58:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/74195282.html
匿名

发表评论

匿名网友

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

确定