I don't understand busy modes of a timer object
13 vues (au cours des 30 derniers jours)
Afficher commentaires plus anciens
TL;DR: What is the difference between timers using the BusyMode options drop and queue, and the ExecutionMode options fixedDelay and fixedRate? Different parts of the documentation seem to say different things.
I am trying to understand the details of how the timer object works, but am having trouble. I am using the timer help page, and the more detailed article "Handling Timer Queuing Conflicts".
I understand that ExecutionMode="fixedSpacing" effectively adds the execution time of the timer callback to the timer period*, which also means that the callback can never be called while the callback is running. I also understand that when ExecutionMode="fixedRate", I need to use BusyMode to prevent callbacks colliding, and that I can use BusyMode="error" to throw an error (or take another action) if that would happen.
However, I am confused by the other two BusyModes, drop and queue, and how they compare to ExecutionMode="fixedDelay".
The timer help page says that BusyMode="drop" will "drop" the new task "when a timer has to execute TimerFcn before the completion of previous execution of the TimerFcn" and that "skipping of TimerFcn calls" is possible. To me, this suggests that an instance of TimerFcn will be skipped and that the timer period immediately begins afresh.
In contrast, BusyMode="queue" will "wait for [the execution] queue to clear, and then enter task in queue", and "adjusts Period property to manage tasks in execution queue". To me, this suggests that the new TimerFcn will start as soon as the old one finishes, and that it will increase its period (perhaps hidden from the user) to avoid having too many instances of TimerFcn in the queue.
When I look at the "Handling Timer Queuing Conflicts" article for more information, however, it gives an example where drop and queue behave identically! Both examples set a timer to run five times, with period 1s and a timer function that takes 1.6s to run. What I expect: when BusyMode="queue", the timer callback occurs every 1.6s; when BusyMode="drop", the timer callback occurs every 2s (the odd-numbered tasks being dropped, because the callback is running). What actually happens: in both cases, the timer callback occurs every 1.6s. This isn't just a bug in my version of Matlab - the actual article has a table describing what should happen, and although a few of the internal details are different, it clearly shows TimerFcn being started every 1.6s (+ queue lag). The description of what happens within the timer doesn't help all that much, since there are some details that I am sure must be mistakes (e.g. for drop, the 4th and 5th calls of the timer function are dropped twice each).
About ExecutionMode="fixedDelay", the timer help page says that the timer period starts "when the timer function callback restarts execution after a time lag due to delays in the MATLAB execution queue" (contrasting with "immediately after the timer callback function is added to the MATLAB execution queue" for ExecutionMode="fixedRate"). I would take this description to mean that fixedDelay works much like fixedRate, except that the timing is less precise because fixedDelay includes an unknown amount of queue delay. However, in the BusyMode section, the page also says, "for other values of ExecutionMode [other than fixedRate], there cannot be overlapping attempts to execute the timer callback function because the delay between executions is always relative to the completion of the previous execution". I don't see how this can be true unless Matlab deliberately adjusts the queue lag to make this happen. Even more confusingly, the diagram actually shows fixedDelay's timer function starting just before the previous call finishing (although it is so close that I'm sure that wasn't intentional). When I test fixedDelay, it seems to operate identically to the fixedRate tests described above (within a few milliseconds for the queue lag). I have attached those demos to this file (run testTimers.m to demonstrate them, first with a period less than the timer function time; then with a period greater than the timer function time).
I may just have completely missed something completely obvious, but I would very much welcome enlightenment!
4 commentaires
Pascal Brunner
le 19 Mai 2023
Modifié(e) : Pascal Brunner
le 19 Mai 2023
David, thank you for summarizing your questions in this post. I've stumbled upon the same issues understanding the differences in queueing, and for the different execution modes.
A (partial) summary of the post for @Jan from my side would be: Why does queueing only become important for the fixedRate execution mode? What happens in fixedDelay mode, if the time of Period + Queue Lag is shorter than the execution time of TimerFcn? In this case, shouldn't there also occur timer queueing conflicts, which have to be addressed by specifying the BusyMode behavior?
Thank you for your help,
Pascal
David Young
le 21 Fév 2024
There is still a significant issue here. The documentation page "Handling Timer Queuing Conflicts" (link in the original question) has tables showing the same behaviour for "drop" and "queue" - and the examples, when run, confirm that the two options do behave the same. In addition, the behaviour of the "drop" option does not correspond to this statement on the page "If the execution queue is not empty, the timer object skips the execution of the callback." But it doesn't, it queues it!
So the question is: is there a bug in timer, or is there something about the "drop" options that just isn't well explained? How does it actually differ from "queue", and how is it supposed to differ from "queue"?
Réponses (2)
Sarthak
le 23 Mar 2023
Hi David,
As per my understanding, the BusyMode option controls what happens if a timer's task takes longer to execute than the timer interval. If BusyMode is set to "drop", the timer will drop any pending tasks if the previous task has not yet completed. If BusyMode is set to "queue", the timer will queue any pending tasks and wait for the previous task to complete before executing the pending task(s).
On the other hand, the ExecutionMode option controls how the timer schedules its tasks. If ExecutionMode is set to "fixedDelay", the timer will execute its task at a fixed interval after the previous task has completed. If ExecutionMode is set to "fixedRate", the timer will execute its task at a fixed rate, regardless of how long the previous task took to execute.
And as far as the example in Handling Timer Queuing Conflicts is concerned, we are creating a timer with a period of 1 second, but a callback that requires atleast 1.6 seconds.
2 commentaires
David Young
le 21 Fév 2024
An answer to the point made almost a year ago by David Szwer regarding BusyMode would be very welcome.
Geerten Kramer
le 19 Avr 2024
Modifié(e) : Geerten Kramer
le 19 Avr 2024
I do fully agree with David that the behaviour of the timer is not what you expect. However there IS a difference between the behaviour of the "drop" and "queue" settings. The thing is that drop only drops taskts after one single attempt to get back on track. Queue does try to keep up with the intended period until it reaches the set rate again (also preserving phase).
To be get more clarity I also made a test script that generates the following image comparing drop with queue:
So task # 4 and 5 have a pause of 1.7 [s] while the rest of the runs take 0.3 [s] the set period is 1 [s]. It is clearly visible thet the drop case drops one task (after task 6). The queue case keeps trying to get back on the original intended starttimes track.
However, in my opinion, it would make more sense if the drop setting would start dropping tasks from task 5, the first that cannot be met. Using a trick, this can be accomplished as is shown in the code below.
function t = mytimer()
t = timer;
t.Period = 1;
t.StartDelay = 1;
t.ExecutionMode = 'fixedRate';
t.TimerFcn = @mytimer_cb;
t.BusyMode = 'drop';
t.TasksToExecute = 12;
t.UserData = 0; % initialize for timekeeping here
start(t)
end
function mytimer_cb(h,~)
startT = tic; % freeze starttime
% only execute the current task if the last task execution time was smaller then the set period.
if(h.UserData<h.Period*0.95) % add 5% slag
% Run the intended timerfn...
end
h.UserData.TaskExecTime = toc(startT); % store the elapsed time of the current task's execution.
end
This will then give the following behaviour in a graph:
In above graph, only task #4 did pause for 3.7 [s] the others 0.3 [s]. Period is set to 1 [s] again.
Now you het the whished behaviour that for the drop+trick case, the start time at #6 is already on the whole second pace again. So effectively 3 tasks got dropped here (Task #3 is started at 3[s], #4 at 4[s], #5 is forced to execute in 0[s] not performing the task using the trick, #6 is excecuted at 8 [s].
Hope this helps understanding timers in Matlab. I would realy advice Mathworks to at least implement the option I now added using the trick at a real nicely implemented option for the BusyMode.
0 commentaires
Voir également
Catégories
En savoir plus sur Code Execution dans Help Center et File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!