我在MATLAB R2013a中遇到了以下问题,由于某些原因我不明白在函数中定义了一个定时器(包括TimerFcn)时没有调用onCleanup函数。
我添加了两个显示问题的最小例子:
首先按预期调用清理例程的工作版本:
function mytest(time)
t = timer();
myclean = onCleanup(@() disp('function ended'));
pause(time);
end
现在是没有调用清理的错误版本(当函数正常结束或按下ctrl + c时都没有)
function mytest2(time)
t = timer();
t.TimerFcn = @(o,s)disp(' ... waiting for some time');
myclean = onCleanup(@() disp('function ends'));
pause(time);
end
我在文档中找不到任何提示,为什么定时器或更具体的TimerFcn定义会改变清理代码的执行?
答案 0 :(得分:5)
首先,发生了什么?
好吧,onCleanup
会返回一个onCleanup对象。这是一个对象,其唯一目的是将析构函数方法设置为@() disp('function ends')
。当对象超出范围(您希望在函数mytest2
的末尾)时,它将被删除,执行析构函数方法,并显示您的消息。我认为这就是你所期望的。
但是当您创建匿名函数@(o,s)disp(' ... waiting for some time')
并将其分配给计时器的TimerFcn
时,它会获取函数mytest2
的整个当前工作空间的副本,包括onCleanup
对象。计时器是在基础工作区(而不是函数工作区)中创建的,并且即使在函数末尾也会保持存在,并且onCleanup
对象随后永远不会超出范围,永远不会被删除,析构函数永远不会运行,你也不会得到你的信息。
请注意:
a = timerfindall; delete(a);
,则会收到您的消息,因为您已将onCleanup
对象与计时器一起明确删除。幸运的是,它很容易解决:
function mytest3(time)
t = timer();
setTimerFcn(t)
myclean = onCleanup(@() disp('function ends'));
pause(time);
end
function setTimerFcn(t)
t.TimerFcn = @(o,s)disp(' ... waiting for some time');
end
现在,当创建匿名函数时,它只需要复制其本地工作区(即来自子函数setTimerFcn
),其中不包含onCleanup
对象。 onCleanup
对象在您期望的范围之外超出范围,一切都很好。
希望有所帮助!
答案 1 :(得分:1)
一种可能的解决方法(由我的同事发现)是将定时器定义为自己的函数。我们不清楚为什么这样可行,但似乎matlab中定时器的定义改变了函数的一些局部变量的上下文,这些变量没有被删除'函数结束后,就像matlab文档(Object Life cycle of variables in Matlab)
所期望的那样下面显示了一个工作示例(它使用定义的计时器在等待时打印简单的状态消息),在Ctrl + c时或当函数结束时,Timer停止。它还确保只有一个名为' timer-mywait'是定义。
function mytest3(time)
% register Cleanup handler
c = onCleanup(@()onCleanup_fcn);
t = mywait_timer(time);
wait(t);
% onCleanup handle function
function onCleanup_fcn
tm = timerfind('Name','timer-mywait');
if ~isempty(tm) && any(strcmpi(get(tm,'running'),'on'))
disp('Stopped running timer ''timer-mywait''!');
stop(tm);
end
end % onCleanup_fcn
end %mytest3
function t = mywait_timer(time)
N = 5;
t = timerfind('Name','timer-mywait');
if isempty(t) % avoid multiple definitions of the same timer
t = timer();
end
t.ExecutionMode = 'fixedRate';
t.TasksToExecute = N+1;
t.Period = str2num(sprintf('%.3f',time/N)); % Matlab complains about more digits!
t.Name = 'timer-mywait';
t.TimerFcn = @(o,s)mywait_timercb();
start(t),
end % mywait_timer
function mywait_timercb()
disp(' still waiting ... ');
end % mywait_timercb