MATLAB中如何实现可容忍异步进程取消的同步结果返回?
解决MATLAB异步计算被取消后同步调用无法获取结果的问题
问题背景
我们有一个高开销的可扩展计算进程,部署在交互式MATLAB环境中,新数据随时可能生成。规则是:新数据到达时,若当前计算正在运行,立即终止它,基于所有现有数据重新启动计算,确保尽早拿到最新全量数据的结果。但触发初始计算的同步函数需要在最终完成的计算结束后拿到结果——哪怕它最初触发的异步进程被后续新数据取消了。
原示例代码的问题:当同步调用result = h.AddData(n).fetchOutputs;后,如果中途点击按钮添加新数据,最初返回的Future会被取消,同步调用直接报错,虽然最终包含n的结果会存入对象,但无法传递给同步进程。
解决方案1:基于Future职责传递的链式等待
核心思路是:每次取消旧Future时,让同步调用绑定的等待逻辑自动转向新启动的Future。给类新增一个跟踪「最终需要等待的Future」的属性,同步调用等待这个属性对应的Future,而非单次AddData返回的临时Future。
修改后的类代码:
classdef ExampleClass < handle properties InputData OutputData end properties (Hidden, Transient) WorkFuture parallel.Future % 新增:跟踪当前最新的、需同步等待的完成态Future LatestCompletionFuture parallel.Future end methods function obj = ExampleClass() uibutton(uifigure, ButtonPushedFcn = @(~,evt) obj.AddData(evt.Source.Parent.CurrentPoint(1))); % 初始化一个已完成的Future,避免首次调用出错 obj.LatestCompletionFuture = parfeval(@() true, 1); obj.LatestCompletionFuture.wait(); end function output = DoWork(obj) pause(2); drawnow output = obj.InputData * 2; end function val = StoreOutput(obj,val) obj.OutputData = val; % 更新LatestCompletionFuture为携带当前结果的完成态Future obj.LatestCompletionFuture = parfeval(@() val, 1); end function F = AddData(obj,val) % 取消未完成的当前计算 if ~isempty(obj.WorkFuture) && ~obj.WorkFuture.isDone() obj.WorkFuture.cancel(); end obj.InputData(end+1) = val; % 启动新异步计算,完成后更新输出和跟踪Future obj.WorkFuture = parfeval(parallel.Pool.empty,@obj.DoWork,1); obj.WorkFuture.afterAll(@obj.StoreOutput,1); F = obj.WorkFuture; end % 新增同步获取最终结果的方法 function result = SyncAddAndGetResult(obj, val) obj.AddData(val); % 等待最新的完成态Future,自动适配后续触发的新计算 obj.LatestCompletionFuture.wait(); result = obj.LatestCompletionFuture.fetchOutputs{1}; end end end
使用方式:直接调用同步方法即可,无需额外处理取消逻辑:
h = ExampleClass; % 同步添加数据并等待最终结果,中途点击按钮不影响返回值 result = h.SyncAddAndGetResult(10);
解决方案2:基于事件通知的等待机制
核心思路是:当计算完成并更新OutputData时,触发自定义事件,同步调用等待该事件触发后读取结果。
修改后的类代码:
classdef ExampleClass < handle properties InputData OutputData end properties (Hidden, Transient) WorkFuture parallel.Future end % 定义计算完成的自定义事件 events CalculationCompleted end methods function obj = ExampleClass() uibutton(uifigure, ButtonPushedFcn = @(~,evt) obj.AddData(evt.Source.Parent.CurrentPoint(1))); end function output = DoWork(obj) pause(2); drawnow output = obj.InputData * 2; end function val = StoreOutput(obj,val) obj.OutputData = val; % 计算完成后触发事件 notify(obj, 'CalculationCompleted'); end function F = AddData(obj,val) if ~isempty(obj.WorkFuture) && ~obj.WorkFuture.isDone() obj.WorkFuture.cancel(); end obj.InputData(end+1) = val; obj.WorkFuture = parfeval(parallel.Pool.empty,@obj.DoWork,1).afterAll(@obj.StoreOutput,1); F = obj.WorkFuture; end % 同步获取结果的方法 function result = SyncAddAndGetResult(obj, val) obj.AddData(val); % 创建监听器并等待事件触发 listener = addlistener(obj, 'CalculationCompleted', @(~,~) true); wait(listener); delete(listener); result = obj.OutputData; end end end
使用方式:和方案1一致,直接调用同步方法即可:
h = ExampleClass; result = h.SyncAddAndGetResult(10);
方案说明
- 两种方案均能实现:同步调用触发计算后,即使初始异步进程被后续操作取消,仍能等待最终完成的计算并获取结果。
- 方案1基于MATLAB并行计算的原生Future逻辑,适合熟悉并行工具链的场景;方案2基于事件通知,逻辑更直观,适合对事件机制更熟悉的开发者。
内容的提问来源于stack exchange,提问作者Will
相关产品推荐
相关产品推荐

