MATLAB interrupting the algorithm by timer

30 visualizaciones (últimos 30 días)
Alexander Voznesensky
Alexander Voznesensky el 14 de Dic. de 2020
Editada: Brian Harris el 6 de Abr. de 2023
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

Respuestas (3)

Jan
Jan el 18 de Dic. de 2020
Editada: Jan el 18 de Dic. de 2020
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 comentarios
Brian Harris
Brian Harris el 6 de Abr. de 2023
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 el 6 de Abr. de 2023
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

Iniciar sesión para comentar.


Joseph Wilson
Joseph Wilson el 14 de Dic. de 2020
Editada: Walter Roberson el 6 de Abr. de 2023
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 comentarios
Joseph Wilson
Joseph Wilson el 15 de Dic. de 2020
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 el 16 de Dic. de 2020
Yes, they are built in matlab functions...

Iniciar sesión para comentar.


Brian Harris
Brian Harris el 6 de Abr. de 2023
Editada: Brian Harris el 6 de Abr. de 2023
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 comentarios
Walter Roberson
Walter Roberson el 6 de Abr. de 2023
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 el 6 de Abr. de 2023
Editada: Brian Harris el 6 de Abr. de 2023
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.

Iniciar sesión para comentar.

Categorías

Más información sobre Loops and Conditional Statements en Help Center y File Exchange.

Productos

Community Treasure Hunt

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

Start Hunting!

Translated by