MATLAB: Help with parfor progress bar using data queue

MATLABparfor

Hello. I am trying to implement a progress par for a parfor loop, following the example shown at
I am using a script instead of a function as shown in the example, but I am unable to adapt the code in a way that works.
My code:
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
afterEach(D, @nUpdateWaitbar);
num_files = 1000;
parfor i = 1:num_files
% do stuff
send(D, [i num_files]);
end
function p = nUpdateWaitbar(input)
p = input(1)/input(2)
waitbar(p, h);
end
Executing this gives
Warning: Unrecognized function or variable 'h'.
which I assume is because the function nUpdateWaitbar doesnt know what 'h' is
but if I try to pass the waitbar handle 'h' into the dataqueue, I get another error:
Cannot convert double value 1000 to a handle
Can you please point me towards what I am doing wrong?
the parfor loop runs fine without the waitbar code so I dont think that is the issue
thanks!

Best Answer

The problem here is that the documentation example is using a nested function, which is able to acces variables in the containing workspace. Your function definition inside a script is a local function, which cannot automatically access the variables in the containing workspace. An alternative fix is to use an anonymous function handle to "bind" in the value of h.
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
% Note anonymous function captures the value of "h" to pass
% in to "nUpdateWaitbar"
afterEach(D, @(data) nUpdateWaitbar(data, h));
num_files = 1000;
parfor i = 1:num_files
% do stuff

send(D, [i num_files]);
end
function p = nUpdateWaitbar(input, h)
p = input(1)/input(2)
waitbar(p, h);
end
This "works", but it isn't right. If you run it you'll see the value flickering about because the parfor loop iterations are not processed in order. So, you need a different approach for nUpdateWaitbar. I would essentially make nUpdateWaitbar contain persistent data and have an "initialisation" mode. Like this:
clear
D = parallel.pool.DataQueue;
h = waitbar(0, 'Please wait ...');
num_files = 1000;
% Dummy call to nUpdateWaitbar to initialise
nUpdateWaitbar(num_files, h);
% Go back to simply calling nUpdateWaitbar with the data
afterEach(D, @nUpdateWaitbar);
parfor i = 1:num_files
% do stuff
% Note we send only an "increment" for the waitbar.
send(D, 1);
end
function p = nUpdateWaitbar(data, h)
persistent TOTAL COUNT H
if nargin == 2
% initialisation mode
H = h;
TOTAL = data;
COUNT = 0;
else
% afterEach call, increment COUNT
COUNT = 1 + COUNT;
p = COUNT / TOTAL;
waitbar(p, H);
end
end