Matlab onCleanup函数未执行

时间:2014-04-06 18:17:56

标签: matlab

我在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定义会改变清理代码的执行?

2 个答案:

答案 0 :(得分:5)

哎哟 - 这很讨厌。它的不是一个错误,但它肯定不是您对文档的期望,而且它不是您想要的。幸运的是,它很容易解决。

首先,发生了什么?

好吧,onCleanup会返回一个onCleanup对象。这是一个对象,其唯一目的是将析构函数方法设置为@() disp('function ends')。当对象超出范围(您希望在函数mytest2的末尾)时,它将被删除,执行析构函数方法,并显示您的消息。我认为这就是你所期望的。

但是当您创建匿名函数@(o,s)disp(' ... waiting for some time')并将其分配给计时器的TimerFcn时,它会获取函数mytest2的整个当前工作空间的副本,包括onCleanup对象。计时器是在基础工作区(而不是函数工作区)中创建的,并且即使在函数末尾也会保持存在,并且onCleanup对象随后永远不会超出范围,永远不会被删除,析构函数永远不会运行,你也不会得到你的信息。

请注意:

  1. 如果您在基本工作区中运行a = timerfindall; delete(a);,则会收到您的消息,因为您已将onCleanup对象与计时器一起明确删除。
  2. 关于匿名函数获取整个工作区副本的这一行为已完整记录,尽管您可能没有意识到这一点,尽管您显然不希望它在此处工作。这很讨厌。
  3. 幸运的是,它很容易解决:

    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