处理计时器队列冲突
在繁忙时段,在多执行情形中,计时器可能需要在先前排队的回调函数执行完毕之前,将计时器回调函数 (TimerFcn
) 添加到 MATLAB® 执行队列中。仅当 ExecutionMode
属性设置为 FixedRate
时,BusyMode
属性才会影响行为。对于 ExecutionMode
的其他值,执行计时器回调函数的多次尝试之间不能存在重叠,因为各次执行之间的延迟始终是相对于前一次执行的完成而言的。
您可以通过将 BusyMode
属性设置为以下模式之一来确定计时器对象如何处理此情形:
Drop 模式(默认值)
如果将 'drop'
指定为 BusyMode
属性的值,计时器对象仅在执行队列为空时才将计时器回调函数添加到该队列中。如果执行队列不为空,计时器对象会不执行回调。
例如,假设创建一个时间段为 1 秒的计时器,但其回调至少需要 1.6 秒,正如此处 mytimer.m
所示的计时器。
function mytimer() t = timer; t.Period = 1; t.ExecutionMode = 'fixedRate'; t.TimerFcn = @mytimer_cb; t.BusyMode = 'drop'; t.TasksToExecute = 5; t.UserData = tic; start(t) end function mytimer_cb(h,~) timeStart = toc(h.UserData) pause(1.6); timeEnd = toc(h.UserData) end
下表介绍了该计时器管理执行队列的方式。
所耗的大致时间(秒) | 计时器操作 | 函数运行 |
---|---|---|
0(计时器启动) | 将 TimerFcn 添加到计时器队列 | 无 |
0 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第一次调用 |
1 | 尝试将 TimerFcn 的第二次调用添加到队列中。TimerFcn 的第一次调用仍在运行,因此第二次调用被丢弃 | |
1.6 | 将 TimerFcn 的第二次调用添加到队列中 | 无 |
1.6 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第二次调用 |
2 | 尝试将 TimerFcn 的第三次调用添加到队列中。TimerFcn 的第二次调用仍在运行,因此第三次调用被丢弃 | |
3.2 | 将 TimerFcn 的第三次调用添加到队列中。第四次调用被丢弃 | 无 |
3.2 + 队列滞后 | 尝试将 TimerFcn 的第四次调用添加到队列中。TimerFcn 的第三次调用仍在运行,因此第四次调用被丢弃 | TimerFcn 的第三次调用 |
3.2 | ||
4.8 | 将 TimerFcn 的第四次调用添加到队列中。第五次调用被丢弃 | 无 |
4.8 + 队列滞后 | 尝试将 TimerFcn 的第五次调用添加到队列中。TimerFcn 的第四次调用仍在运行,因此第五次调用被丢弃 | TimerFcn 的第四次调用 |
4.8 | ||
6.4 | 将 TimerFcn 的第五次调用添加到队列中。 | 无 |
6.4 + 队列滞后 | TasksToExecute 的值为 5,因此 mytimer 没有更多要执行的回调。 | TimerFcn 的第五次调用 |
8 |
错误模式
BusyMode
属性的 'error'
模式类似于 'drop'
模式:在这两种模式下,计时器仅允许执行队列中有一个回调实例。在 'error'
模式下,当队列不为空时,计时器会调用您使用 ErrorFcn
属性指定的函数,然后停止处理。当前运行的回调函数执行完毕,但不执行队列中的回调。
例如,修改 mytimer.m
(上一节中所述)以使其包含错误处理函数并将 BusyMode
设置为 'error'
。
function mytimer() t = timer; t.Period = 1; t.ExecutionMode = 'fixedRate'; t.TimerFcn = @mytimer_cb; t.ErrorFcn = @myerror; t.BusyMode = 'error'; t.TasksToExecute = 5; t.UserData = tic; start(t) end function mytimer_cb(h,~) timeStart = toc(h.UserData) pause(1.6); timeEnd = toc(h.UserData) end function myerror(h,~) disp('Reached the error function') end
下表介绍了该计时器管理执行队列的方式。
所耗的大致时间(秒) | 计时器操作 | 函数运行 |
---|---|---|
0(计时器启动) | 将 TimerFcn 添加到计时器队列 | 无 |
0 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第一次调用 |
1 | 尝试将 TimerFcn 的第二次调用添加到队列中。TimerFcn 的第一次调用仍在运行,因此 myerror 将在 TimerFcn 的第一次调用完成时排队 | |
1.6 | 将 myerror 添加到队列中 | 无 |
1.6 + 队列滞后 | 不执行任何操作。 | 调用 myerror |
Queue 模式
如果您指定 'queue'
,则计时器对象会一直等到当前执行的回调函数执行完毕,然后才再次排队执行计时器回调函数。
在 'queue'
模式下,计时器对象会尝试使每次执行之间的平均时间等于 Period
属性中指定的时间量。如果计时器对象必须等待的时间比 Period
属性中指定的每次执行计时器函数回调之间的时间更长,则它会缩短该时间段以便后续执行弥补这段时间。
例如,修改 mytimer.m
(如前一节所述),使 BusyMode
设置为 'queue'
。
function mytimer() t = timer; t.Period = 1; t.ExecutionMode = 'fixedRate'; t.TimerFcn = @mytimer_cb; t.ErrorFcn = @myerror; t.BusyMode = 'queue'; t.TasksToExecute = 5; t.UserData = tic; start(t) end function mytimer_cb(h,~) timeStart = toc(h.UserData) pause(1.6); timeEnd = toc(h.UserData) end function myerror(h,~) disp('Reached the error function') end
下表介绍了该计时器管理执行队列的方式。
所耗的大致时间(秒) | 计时器操作 | 函数运行 |
---|---|---|
0 | 开始首次执行回调。 | 无 |
0 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第一次调用 |
1 | 尝试开始第二次回调执行。第一次执行未完成,执行队列仍为空。 | |
1.6 | 计时器将第二次回调添加到队列中。 | 无 |
1.6 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第二次调用 |
2 | 尝试添加第三次回调执行。第二次执行未完成,执行队列仍为空。 | |
3 | 尝试开始第四次回调执行。第二次执行未完成,执行队列仍为空。 | |
3.2 | 完成第二次回调执行。计时器向队列添加第三次和第四次回调,并执行第三次回调。执行队列包含第四次回调。 | 无 |
3.2 + 队列滞后 | 不执行任何操作。 | TimerFcn 的第三次调用 |
4 | 尝试执行第五次回调也是最后一次回调。第三次执行未完成。执行队列包含第四次回调。 | |
4.8 | 完成第三次回调执行。计时器将第五次执行添加到队列中,并执行第四次回调。执行队列包含第五次回调。 | 无 |
4.8 + 队列滞后 | TasksToExecute 的值为 5,因此 mytimer 没有更多要执行的回调。 | TimerFcn 的第四次调用 |
6.4 | 无 | |
6.4 + 队列滞后 | TimerFcn 的第五次调用 | |
8 |