Main Content

afterEach

Specify a function to invoke after each parallel.Future completes

Description

example

outputFuture = afterEach(futures,funtocall,nout) automatically evaluates funtocall on the output arguments of each of the elements in futures as they become ready. afterEach calls funtocall with nout output arguments and produces outputFuture to hold the outputs.

A useful application for afterEach is to update user interfaces such as plots and apps during parallel computations using parfeval. For example, you can send several computations to workers using parfeval and update your user interface when each of those finishes using afterEach.

example

outputFuture = afterEach(futures,funtocall,nout,'PassFuture',passFuture) behaves the same if passFuture is false. If passFuture is true, afterEach invokes funtocall on each element in futures, and not on their output arguments. This happens even if elements of futures encountered errors.

Examples

collapse all

You can use afterEach to automatically invoke functions on each of the results of parfeval computations.

Use parfeval to compute random vectors in the workers. With default preferences, parfeval creates a parpool automatically if there is not one already created.

for idx = 1:10
    f(idx) = parfeval(@rand, 1, 1000, 1);
end

Display the maximum element in each of those vectors after they are created. afterEach executes the function handle on the output of each future when they become ready.

afterEach(f, @(r) disp(max(r)), 0);
    0.9975

    0.9990

    0.9982

    0.9991

    0.9982

    0.9998

    0.9999

    0.9986

    0.9996

    0.9990

You can combine afterEach and afterAll to automatically invoke more functions on the results of futures. Both afterEach and afterAll generate future variables that can be used again in afterEach and afterAll.

Use parfeval to compute random vectors in the workers. With default preferences, parfeval creates a parpool automatically if there is not one already created.

for idx= 1:10
    f(idx) = parfeval(@rand, 1, 1000, 1);
end
Starting parallel pool (parpool) using the 'local' profile ...
connected to 8 workers.

Compute the largest element in each of those vectors when they become ready. afterEach executes the function handle on the output of each future when they become ready and creates another future to hold the results.

maxFuture = afterEach(f, @(r) max(r), 1);

To compute the minimum value among them, call afterAll on this new future. afterAll executes a function on the combined output arguments of all the futures after they all complete. In this case, afterAll executes the function min on the outputs of maxFuture after completing and creates another future to hold the result.

minFuture = afterAll(maxFuture, @(r) min(r), 1);

You can fetch the result using fetchOutputs. fetchOutput waits until the future completes to gather the results.

fetchOutputs(minFuture)
ans = 0.9973

You can check the result of afterEach by calling fetchOutputs on its future variable.

fetchOutputs(maxFuture)
ans = 10×1

    0.9996
    0.9989
    0.9994
    0.9973
    1.0000
    1.0000
    0.9989
    0.9994
    0.9998
    0.9999

This example shows how to update a user interface as computations complete. When you offload computations to workers using parfeval, all user interfaces are responsive while workers perform these computations. In this example, you use waitbar to create a simple user interface.

  • Use afterEach to update the user interface after each computation completes.

  • Use afterAll to update the user interface after all the computations complete.

Use waitbar to create a figure handle, h. When you use afterEach or afterAll, the waitbar function updates the figure handle. For more information about handle objects, see Handle Object Behavior.

h = waitbar(0,'Waiting...');

Use parfeval to calculate the real part of the eigenvalues of random matrices. With default preferences, parfeval creates a parallel pool automatically if one is not already created.

for idx = 1:100
    f(idx) = parfeval(@(n) real(eig(randn(n))),1,5e2); 
end

You can use afterEach to automatically invoke functions on each of the results of parfeval computations. Use afterEach to compute the largest value in each of the output arrays after each future completes.

maxFuture = afterEach(f,@max,1);

You can use the State property to obtain the status of futures. Create a logical array where the State property of the futures in f is "finished". Use mean to calculate the fraction of finished futures. Then, create an anonymous function updateWaitbar. The function changes the fractional wait bar length of h to the fraction of finished futures.

updateWaitbar = @(~) waitbar(mean({f.State} == "finished"),h);

Use afterEach and updateWaitbar to update the fractional wait bar length after each future in maxFuture completes. Use afterAll and delete to close the wait bar after all the computations are complete.

updateWaitbarFutures = afterEach(f,updateWaitbar,0);
afterAll(updateWaitbarFutures,@(~) delete(h),0);

Use afterAll and histogram to show a histogram of the results in maxFuture after all the futures complete.

showsHistogramFuture = afterAll(maxFuture,@histogram,0);

When computations for future variables result in an error, by default, afterEach does not evaluate its function on the elements that failed. If you want to handle any errors, for example, you have a user interface that you want to update, you can use the name-value pair PassFuture. When set to true, the future variable is passed to the callback function. You can call fetchOutputs on it, process the outputs, and handle any possible errors.

Send computations to the workers using parfeval. With default preferences, parfeval creates a parpool automatically if there is not one already created. If your parfeval computations result in an error, the future variable errors, and its Error property reflects it.

errorFuture = parfeval(@(n) randn(n), 0, 0.5);
wait(errorFuture);
errorFuture.Error
ans = 
  ParallelException with properties:

     identifier: 'MATLAB:NonIntegerInput'
        message: 'Size inputs must be integers.'
          cause: {}
    remotecause: {[1×1 MException]}
          stack: [1×1 struct]

If you use afterEach on that future, the callback function is not evaluated on those elements in the future that errored. In the code below, the msgbox is not executed because the future errors.

afterEach(errorFuture, @() msgbox('Operation completed'), 0);

To handle futures that result in errors, use the name-value pair PassFuture when calling afterEach. The future variable is passed to the callback function instead of its outputs. Call fetchOutputs on the future variable, and process its outputs. If the future results in an error, fetchOutputs throws an error that you can catch and handle. The following code shows an error dialog box.

afterEach(errorFuture, @handleError, 0, 'PassFuture', true);

function handleError(f)
    try
        output = fetchOutputs(f);
        % Do something with the output
    catch
        errordlg('Operation failed');
    end
end

Input Arguments

collapse all

Futures, specified as an array of parallel.Future. funtocall is invoked on each of its elements when they become ready. You can use parfeval to create a future.

If an element of futures encounters an error, funtocall is not evaluated for that element of futures, however, it is evaluated for other elements of futures that do not encounter errors. To see if there are any futures with errors, you can check the Error property of outputFuture. This property is an empty cell array if there are no errors. If there are errors, it is a cell array that contains as many cells as futures in futures. A cell contains an error if the corresponding element of futures encountered an error, and is otherwise empty. If you cancel an element of futures, this results in the same behavior as if the element encountered an error.

Example: future = parfeval(@rand,1,1000,1); afterEach(future,@max,1);

Data Types: parallel.Future

Function to execute, specified as a function to call on the output arguments of each of the futures in futures when they become ready. funtocall is evaluated on the MATLAB® client and not on the parallel pool workers.

Example: funtocall = @max

Data Types: function handle

Number of outputs, specified as an integer, expected from funtocall.

Example: afterEach(futures,@max,1)

Data Types: scalar

Indicator, specified as a logical scalar that determines the type of input arguments to funtocall. If set to true, each future in futures is passed to funtocall. Otherwise, the output arguments of each future in futures are passed to funtocall. This argument is optional and is false by default.

You can use this approach if you want to handle any errors. Set passFuture to true so that afterEach invokes funtocall on each element in futures, even if they encountered errors. You must call fetchOutputs on the input argument to funtocall to extract the results. If there are any futures with errors, fetchOutputs throws an error that you can catch and handle.

Example: afterEach(futures,@(f) disp(fetchOutputs(f)),0,'PassFuture',true)

Data Types: logical scalar

Output Arguments

collapse all

Future, returned as a parallel.Future to hold the results of evaluating funtocall on each of the futures in futures when they become ready. To extract the results, call fetchOutputs on outputFuture.

Tips

  • Use afterEach on any of the futures returned from parfeval, parfevalOnAll, afterAll, afterEach, or an array containing a combination of them. For example, use afterEach to automatically invoke more functions on the results of another afterAll or afterEach. You can invoke afterEach on futures before and after they finish.

  • Use cancel on a future returned from afterEach to cancel its execution. If you invoke afterEach on a canceled future, afterEach behaves the same way as if the future had an error.

Introduced in R2018a