Getting my head around the choice between sleeping and 'tickers'

August 1, 2020

If you have a program that wants to do something periodically, say once every ten seconds, the obvious and simplest way is to just sleep for ten seconds after each time you do the thing. For example:

while True:
  collect_device_stats()
  publish_device_stats()
  time.sleep(10)

Some languages and libraries also offer an API that I've seen called a 'ticker'; Go, for example, has time.Ticker. A ticker signals or invokes you somehow every time interval, such as every ten seconds. With a ticker, you could write the above (in Go) as something like:

ticker := time.NewTicker(10*time.Second)
for {
  t := <- ticker.C
  collect_device_stats(t)
  publish_device_stats(t)
}

The big difference between the two is that a ticker is indifferent to how long it took you to do your work. If collecting and publishing device stats takes two seconds, the sleep approach means that you will do this once every twelve seconds (two seconds for the work followed by ten seconds of sleeping). The ticker approach will collect and publish once every ten seconds, because it signals you every ten seconds even if it took you two seconds to collect and publish device stats.

While this may make tickers sound great, this behavior has potential downsides. Which one you want depends partly on what your reasons are for sleeping or delaying. In particular, if you're sleeping to limit the load your actions create, then a ticker may not be what you want, precisely because it's indifferent to how long your work took. If your work takes nine seconds and you sleep afterward for ten, you're keeping things less than 50% busy. If your work takes nine seconds and you have a ten second ticker, you're keeping things 90% busy; one second after you finish your work the ticker will go off again and start it all over.

Tickers are great for things you want to happen every N seconds, even if they take a bunch of time (or a variable amount of time). Sleeping is great for things you want to happen no more often than once every N seconds. However, the difference between the two only matters if what you're doing does (or may) take an appreciable amount of time compared to the tick (or sleep) interval; if it isn't going to, you might as well use whichever API is more idiomatic and convenient, although tickers will probably make things more regular and precise.

(This set of thoughts was sparked by poking around in some Python code that used the sleep() idiom and then thinking about how it would probably use a ticker in Go. (Well, in Go it would have a better API for what it's doing, but apart from that.))

Written on 01 August 2020.
« Putting some extra 'obvious' information into our temperature alerts
The issue of how to propagate some errors in our Django web app »

Page tools: View Source, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Sat Aug 1 23:24:41 2020
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.