MATLAB interrupting the algorithm by timer

22 ビュー (過去 30 日間)
Alexander Voznesensky
Alexander Voznesensky 2020 年 12 月 14 日
編集済み: Brian Harris 2023 年 4 月 6 日
Hi! I need to set a time limit on an algorithm. I want to throw an error and stop while cycle in try block after 3 seconds, then I want to handle the error in catch block. MATLAB gives me the following diagnostics: Error while evaluating TimerFcn for timer 'timer-2'. Text message 'Error!' is displayed, but error isn't thrown and while cycle is still running. Text message 'Error is caught!' isn't displayed. Could you help me?
t = timer;
t.StartDelay = 3;
t.TimerFcn = @(myTimerObj, thisEvent)error('Error!');
start(t)
a=0;
try
while(1)
if a==1
break;
end
end
stop(t);
delete(t);
catch
stop(t);
delete(t);
disp('Error is caught!')
end

回答 (3 件)

Jan
Jan 2020 年 12 月 18 日
編集済み: Jan 2020 年 12 月 18 日
The timer runs in its own thread. The execution of the timer callback is correctly stopped by the error command, but not the regular processing of other Matlab functions.
Which function to you want to interrupt? If it is e.g. an ODE integrator, you can check the value of a flag in the event function:
[value,isterminal,direction] = myEventsFcn(t,y)
position = y(1); % The value that we want to be zero
isterminal = 0; % Halt integration
direction = 0;
Terminator = getappdata(groot, 'TheTerminator');
if Terminator
isterminal = 1;
end
Now you can set this flag by setappdata(groot, 'TheTerminator') from your TIMER callback.
Many builtin functions allow such requests during the processing, but some doesn't. E.g. a huge linear algebra computation cannot be triggered by this method. Then for longer operations it can be an option to start a 2nd instance of Matlab and kill it after a certain time. For a 3 sec limit this will be inefficient, because starting Matlab takes longer already. But for some minutes this would work.
Which operating system are you using?
runtime = java.lang.Runtime.getRuntime();
process = runtime.exec('matlab -nodisplay -nosplash -nodesktop -r "YourFunction; exit"');
pause(100); % Or do what you want
try
exitCode = process.exitValue();
disp('Process has finished.');
catch ME
% Still running...
process.destroy(); % Sorry, I give up
end
  4 件のコメント
Brian Harris
Brian Harris 2023 年 4 月 6 日
This seems to spawn a process faster than trying to start a parpool; and has fewer (different? restirctions... can spawn plots for e.g.)
Brian Harris
Brian Harris 2023 年 4 月 6 日
OK, follow-up question. If I'm using a parfeval I can get the output stream from the subprocess via
f = parfeval(@()disp('hi'), 0)
f.Diary
% ans =
% 'hi
% '
But, how do I do this with "process"? Digging in a bit, it looks like we can get the input stream (what the subprocess puts out...) via:
in_strm = process.getInputStream();
But getting the same kind of "diary" data is a bit involved... and doesn't seem to be working. I pieced together this from another answer from Benjamin Davis:
runtime = java.lang.Runtime.getRuntime();
process = runtime.exec('matlab -nodesktop -nosplash -nodesktop -r "foo; exit"');
% pause(30); % Or do what you want
in_strm = process.getInputStream();
% out_strm = process.getOutputStream();
getMethod_args = javaArray('java.lang.Class',3);
byteArrayName = '[B';
getMethod_args(1) = java.lang.Class.forName(byteArrayName);
getMethod_args(2) = java.lang.Integer.TYPE;
getMethod_args(3) = java.lang.Integer.TYPE;
m_read = in_strm.getClass().getMethod('read',getMethod_args);
read_args = java.util.ArrayList();
% num_available = in_strm.available();
% buf_size = max(1024, num_available);
buf_size = 1024;
buf_ptr = 0;
while process.isAlive()
read_args.add(zeros(1, buf_size, 'int8'));
read_args.add(int32(buf_ptr));
read_args.add(int32(buf_size));
disp('this seems to block...');
n_read = m_read.invoke(in_strm, read_args.toArray());
disp('... this does not print until after the process is killed');
out = char(read_args.get(0));
out = out(:)';
out = out(1:n_read);
disp(out);
buf_ptr = n_read;
fprintf('alive... %s\n', datetime)
pause(1);
end
try
exitCode = process.exitValue();
disp('Process has finished.');
catch ME
% Still running...
process.destroy(); % Sorry, I give up
end
But It the "avaliable" flag is always 0, so I get nothing out... am I barking up the wrong stream?
For completeness, foo.m:
function foo()
for i = 1:10
disp(i);
pause(1);
end
disp('Done');
end

サインインしてコメントする。


Joseph Wilson
Joseph Wilson 2020 年 12 月 14 日
編集済み: Walter Roberson 2023 年 4 月 6 日
clear;clc
timerval = tic;
while 1
endval = toc(timerval);
if endval>=3
break
end
end
%tic/toc are built in matlab functions to count time. This utilizes them to count seconds for length of time running through the loop
  5 件のコメント
Joseph Wilson
Joseph Wilson 2020 年 12 月 15 日
Are those slow functions written by you or are they built in matlab functions that your can't change? If they are written by you, you can add in time checking sections inside those functions to break out of the function if it has taken too long. If they are not able to be editted by you, you might be out of luck with this method.
This is a similar problem if you cannot edit the functions.
I do not think timer has the ability to end a function like you want it to.
Alexander Voznesensky
Alexander Voznesensky 2020 年 12 月 16 日
Yes, they are built in matlab functions...

サインインしてコメントする。


Brian Harris
Brian Harris 2023 年 4 月 6 日
編集済み: Brian Harris 2023 年 4 月 6 日
If you are ok with the limitations of a parfeval call (need parallel processing toolbox, any graphics produced by the calling function are surpressed, ...) you can easily cancel the call without spawning a new matlab instance.
localPool = gcp('nocreate');
if isempty(localPool)
% the parpool is a singlton, so if its not running, start it.
localPool = parpool("local", 2, 'IdleTimeout', 24 * 60);
end
% To get outputs call "fetchOutputs(f)", but this blocks the main thread, so
% instead you can montor f.Result in a killable loop, timer or listener...
f = parfeval(localPool, @pause, 0, Inf);
% for now we'll just kill the execution after 3 seconds
death_clock = timer( ...
'ExecutionMode', 'singleShot', ...
'StartDelay', 3.0, ...
'TimerFcn', @(~, ~)cancel(f) ...
);
% Show the FevalFuture object
disp(f);
fprintf('Started death clock at: %s\n', datetime);
death_clock.start(); % start the death clock
% Monitor for completion/termination
while ~strcmp(f.State, 'finished')
fprintf('waiting to finish... %s\n', datetime);
pause(1);
end
fprintf('Finished... %s\n', datetime);
disp(f);
  4 件のコメント
Walter Roberson
Walter Roberson 2023 年 4 月 6 日
You might also be interested in experimenting with the newer backgroundPool and parfeval -- which can be canceled as well. background pools should normally be faster than parpool but have some restrictions on what they can do.
Brian Harris
Brian Harris 2023 年 4 月 6 日
編集済み: Brian Harris 2023 年 4 月 6 日
Thanks Walter. I'll have to give that a shot. Sadly my current project is stuck with R2020a (backgroundPool was introduced in R2021b); will try that out on the next project! Note, using parpool('threads') is supposedly similar, so I'll give that a try; though there are some additonal limitations over the local.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeLoops and Conditional Statements についてさらに検索

製品

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by