Thread::sleep(x) vs timers in rules

Hi,

So, I’ve seen various examples of the use of both timers and Thread::sleep in rules. And indeed my rules are peppered with timers myself.

Most of the coding I’ve done in the past has been PHP, where very often I would use ‘sleep’ in order to wait for things.

My question is then, what are the pros and cons of one approach over another? I’ve read a bit about each in Java and the Xtend documentation. But I wondered if anyone (@rlkoshak?!) could provide any insight here?

Thanks

Chris

1 Like

I use both, timers are great, but one thing I ran into is that they spawn a thread so your rule keeps executing. If your trying to sleep and not move forward in your rule until your done use Thread::sleep.

1 Like

If you want to do something in the future but don’t want to wait around for it in your rule (e.g. you have more stuff you want to do after kicking off the timer) or you may want to reschedule when the timer triggers or cancel it based on subsequent events a timer is the appropriate thing to use.

If you want to wait for a bit and block the rest of the rule from executing until you are done waiting then Thread::sleep is what you want to use.

The biggest difference, as @sipvoip describes, is that Thread::sleep blocks the running of the current rule for a certain number of milliseconds and then continues executing the rest of the logic in the rule. A Timer spins up a separate thread which starts executing in the background after the Timer goes off.

Thus, a call to Thread::sleep(1000) will not return until a second has passed. A call to createTimer(now.plusSeconds(1), [| logInfo("Timer", "My Timer!")] will exit immediately and continue executing the rule, possibly even exiting the rule. Then when one seconds has passed “My Timer!” will appear in your log.

I will use Thread::sleep

  • The amount of time I want to wait is short (seconds)
  • I want to execute some code after the sleep (i.e. I’m waiting for something to happen such as Persistence catching up before executing the rest of a rule’s logic)
  • I have no plans to change what I do or set another Thread::sleep should the state not be just right after the sleep

I will use a Timer

  • The amount of time I want to wait is relatively long (minutes)
  • I may want to cancel or reschedule the code based on subsequent events
  • I have additional stuff I want to do in a rule after the timer is created that doesn’t need to wait for the timer code to execute

To be honest the vast majority of the time I use a Timer but there are a couple places where I use Thread::sleep. Most notably when using my hack to figure out which is the most recently updated Item in a Group (a hack to figure out which Item in a Group triggered an update but it depends on lastUpdate so I have to wait for Persistence to save the most recent values) or when I have a set sequence of events such as one case where when one of my Raspberry Pis is detected offline I turn off its outlet, sleep for a second, then turn it back on.

Edit: since this posting is getting a lot of attention right now I’ll add that in the year since I’ve written it a new Expire Binding was developed. I highly recommend whereever possible using the Expire binding instead of Timers as it is easier to use and the code is much cleaner and less error prone.

19 Likes

Thank you @rlkoshak.

Super succinct and exactly what I wanted to clarify. I had a feeling you were the man with the answer.

Many many thanks once again

Chris

@rlkoshak (as usual) has a good summary. I’d add one more use case for Thread::sleep — when you positively need to make sure other rules which might have been triggered are completed. Specifically, when you are using PROXY items and need to sync the states of PROXY and “real” versions because either one might be the driver of change in the other, you will need a ::sleep to block race conditions–i.e. change in proxy triggers change in real item, change in real item triggers change in proxy. Race condition can be particularly entertaining (in a “poltergeist” sense), but not what you want.

Use as follows:

rule "PXY_SYNCH: Update Kitchen PROXY state"
    when
        Item sw_LIGHT_Kitchen changed
    then
        Thread::sleep(10)
        postUpdate(sw_LIGHT_Kitchen_PROXY,sw_LIGHT_Kitchen.state)
end

Hi friends,
I would like to create a Timer rule to automatically turn off my Water Boiler Item 45 minutes after I turn it ON.

As indicated by @rlkoshak in an earlier post, when dealing with rule timers longer than a few seconds, a Timer command will be better suited than a Thread::sleep (xxxx) command.

Can anyone please help me convert this Thread rule to a Timer based rule?
Unfortunately, I’m not proficient enough with coding to know how to do it myself. Hope you can help :pray: :pray:

rule "Turn Off Water Boiler"
when
         Item Boiler_Switch changed from OFF to ON
then
        Thread::sleep (2700000)
        sendCommand(Water_Boiler, OFF)

end

This is the most basic of Timer requirements. Starting from the example in the docs.

var Timer myTimer = createTimer(now.plusMinutes(5), [ |
    logInfo("rules", "Timer activated")
])
``

You should be able to convert your code to use a timer with this example. Give it a try and if it doesn't work come back with questions. If you need more examples, searching for "createTimer" will reveal thousands of examples here on the forum.

In the future, it is *way way way* better to open a new thread than to reopen a five year old thread.
1 Like

Thank you, @rlkoshak :pray: :pray:
I managed to make it work after some trial and error :slight_smile:

In the future, it is way way way better to open a new thread than to reopen a five year old thread.

Will do. I thought since my question was related to this thread’s subject, it would be preferable to write in it, instead of opening a similar thread. I guess my logic was wrong. I’ll make sure to open new threads in the future, in case the earlier threads are very old.

Thanks again for your help.

The easy Way is the Expire Binding :grinning:

The Timer Way is this:

rule "Turn Off Water Boiler"
when
  Item Boiler_Switch changed from OFF to ON
then
  var Timer boilerTimer = createTimer(now.plusMinutes(45), [ |
      Water_Boiler.sendCommand(OFF)
  ])
end
1 Like

Thank you, Kevin.
This helps :pray: