Main Content

afterEachafterAll を使用したコールバック関数の実行

parfevalparfevalOnAllafterEach、または afterAll を使用してバックグラウンドまたは並列プールで関数を実行する際には、Future を作成します。afterEachafterAll を使用すると、1 つ以上の Future オブジェクトが終了した後に自動でコールバック関数を実行することができます。

  • afterEach を使用する場合、MATLAB® は各 Future オブジェクトの終了後にコールバック関数を実行します。Future の配列に M 個の要素がある場合、MATLAB クライアントはコールバック関数を M 回実行します。

  • afterAll を使用する場合、MATLAB はすべての Future オブジェクトの終了後にコールバック関数を実行します。Future の配列に M 個の要素がある場合、MATLAB クライアントはコールバック関数を 1 回だけ実行します。

parfeval の計算に対する afterEach の呼び出し

afterEach を使用して、parfeval の各計算結果に対して自動的に関数を呼び出すことができます。

parfeval を使用して、ワーカーで乱数ベクトルを計算します。既定の基本設定では、parpool がまだ作成されていなければ、parfeval によって自動的に作成されます。

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

これらのベクトルが作成されたら、それぞれの最大要素を表示します。afterEach は、各 Future が使用可能になると、それぞれの出力に対して関数ハンドルを実行します。

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

parfeval の計算に対する afterAll の呼び出し

afterAll を使用して、parfeval の計算の出力すべての結合に対して自動的に関数を呼び出すことができます。

parfeval を使用して、ワーカーで乱数ベクトルを計算します。既定の基本設定では、parpool がまだ作成されていなければ、parfeval によって自動的に作成されます。

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

ベクトルの作成後、これらすべての最大要素を表示します。afterAll は、すべての Future が使用可能になったら、これらの出力の結合に対して関数ハンドルを実行します。

afterAll(f, @(r) fprintf("Maximum element is %1.4f\n",max(r)), 0);

afterEach afterAll の組み合わせ

afterEach afterAll を組み合わせて、Future の結果に対して、さらに関数を自動的に呼び出すことができます。afterEachafterAll はいずれも、afterEachafterAll で再利用可能な future 変数を生成します。

parfeval を使用して、ワーカーで乱数ベクトルを計算します。既定の基本設定では、parpool がまだ作成されていなければ、parfeval によって自動的に作成されます。

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

ベクトルが使用可能になったら、各ベクトルの最大要素を計算します。afterEach は、Future が使用可能になったら、各 Future の出力に対して関数ハンドルを実行し、別の Future を作成して結果を格納します。

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

これらの最小値を計算するために、この新しい Future について afterAll を呼び出します。afterAll は、すべての Future が完了した後、すべての Future の出力引数の結合に対して関数を実行します。この場合、afterAll は、maxFuture の完了後にその出力に対して関数 min を実行し、別の Future を作成して結果を格納します。

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

この結果は、fetchOutputs を使用して取得できます。fetchOutput は、Future が結果の収集を完了するまで待機します。

fetchOutputs(minFuture)
ans = 0.9970

fetchOutputs を future 変数で呼び出して、afterEach の結果を確認できます。

fetchOutputs(maxFuture)
ans = 10×1

    0.9984
    0.9996
    1.0000
    0.9999
    0.9999
    0.9970
    0.9999
    0.9998
    0.9998
    0.9997

afterEachafterAll を使用したユーザー インターフェイスの非同期更新

この例では、計算の完了時にユーザー インターフェイスを更新する方法を説明します。parfeval を使用して計算をワーカーにオフロードすると、ワーカーが計算を実行中にすべてのユーザー インターフェイスが応答可能な状態になります。waitbar を使用すると単純なユーザー インターフェイスを作成できます。

  • それぞれの計算が完了するたびにユーザー インターフェイスを更新するには、afterEach を使用します。

  • すべての計算が完了した後にユーザー インターフェイスを更新するには、afterAll を使用します。

waitbar を使用して Figure ハンドル h を作成します。afterEach または afterAll を使用すると、関数 waitbar は Figure ハンドルを更新します。ハンドル オブジェクトの詳細については、ハンドル オブジェクトの動作を参照してください。

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

parfeval を使用して、乱数行列の固有値の実数部を計算します。既定の基本設定では、並列プールがまだ作成されていなければ、parfeval によって自動的に作成されます。効率性を高めるため、Future オブジェクトの配列を事前に割り当てます。

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

afterEach を使用して、parfeval の各計算結果に対して自動的に関数を呼び出すことができます。f 内の各 future が完了するたびに、afterEach を使用して別の future オブジェクトのセットをスケジュールし、各出力配列内の最大値を計算します。

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

State プロパティを使用して、future のステータスを取得できます。ウェイト バー h の割合の長さを、実行が終了した Future オブジェクトの割合に更新する無名関数を定義します。無名関数 updateWaitbar は、f の対応する Future オブジェクトの State プロパティが "finished" の場合に要素が true である logical 配列の平均値を計算します。

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

maxFuture の各 future が完了するたびに、afterEachupdateWaitbar を使用してウェイト バーの割合の長さを更新します。すべての計算が完了したら、afterAlldelete を使用してウェイト バーを閉じます。

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

すべての future が完了したら、afterAllhistogram を使用して maxFuture の結果のヒストグラムを表示します。

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

future 変数のエラーの処理

future 変数の計算でエラーが発生した場合、既定で afterEach はエラーの発生した要素についてその関数を評価しません。たとえば、更新するユーザー インターフェイスがあるなど、任意のエラーを処理する場合は、名前と値のペア PassFuture を使用できます。true に設定されている場合、future 変数はコールバック関数に渡されます。これに対して fetchOutputs を呼び出し、出力を処理して、考えられるエラーすべてに対処できます。

parfeval を使用してワーカーに計算を送信します。既定の基本設定では、parpool がまだ作成されていなければ、parfeval によって自動的に作成されます。parfeval の計算でエラーが発生した場合、future 変数はエラーになり、その Error プロパティに反映されます。

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]
     Correction: []

その Future に対して afterEach を使用すると、エラーになった Future の要素についてコールバック関数は評価されません。以下のコードでは、Future のエラーのため、msgbox が実行されません。

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

結果がエラーになる Future を扱うには、afterEach を呼び出すときに名前と値のペア PassFuture を使用します。出力ではなく future 変数がコールバック関数に渡されます。future 変数に対して fetchOutputs を呼び出し、その出力を処理します。Future でエラーが発生すると、fetchOutputs によりエラーがスローされます。これをキャッチして処理できます。以下のコードはエラー ダイアログ ボックスを表示します。

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

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

参考

| | | |