首页 > 解决方案 > parfor 不会将更改传播到句柄类

问题描述

在 R2018b 中,考虑以下玩具类:

classdef MyObj < handle

    properties
        use_parallel = true
        test_value   = NaN
    end

    methods
        function myMethod(obj)

            % Call one of the nested functions below:
            if all([obj.use_parallel])
                parallel();                
                disp('Parallel (inside myMethod):')
                [obj.test_value]
            else
                sequential();
                disp('Sequential (inside myMethod):')
                [obj.test_value]
            end

            % Sequentially assign some values
            function sequential()                
                for ii = 1:numel(obj)
                    obj(ii).test_value = ii; end
            end

            % Assign some values in parallel
            function parallel()
                parfor ii = 1:numel(obj)
                    set_value(obj(ii),labindex());
                    obj_copy(ii) = obj(ii);
                end
                obj = obj_copy;
            end

        end
    end
end

% parfor requires subfunction (and not nested function):
function set_value(obj,index)
    obj.test_value = index;
end

尽管这个问题与这个问题和这个问题非常相似,但它们的根本问题本质上是文档中概述的限制的一些变体:

您可以将句柄对象作为输入发送到 parfor 循环的主体。但是,在循环迭代期间为处理工作人员上的对象所做的任何更改都不会自动传播回客户端。也就是说,循环内部所做的更改不会在循环之后自动反映

但是,据我所知,上面的玩具类符合parfor切片规则以及这些关于句柄类的细节。据我了解,它应该因此正确地将修改后的复制objmyMethod's工作区。

但是,运行以下命令:

clc

% Assign sequentially
M(3) = MyObj();
[M.use_parallel] = deal(false);
M.myMethod();
disp('Sequential (outside class):')
[M.test_value]

disp(' ')

% Assign in parallel 
N(3) = MyObj();
[N.use_parallel] = deal(true);
N.myMethod();
disp('Parallel (outside class):')
[N.test_value]

给我parpool的 6 名工人:

Sequential (inside myMethod):
ans =
     1     2     3   % <- OK
Sequential (outside class):
ans =
     1     2     3   % <- OK. Nothing unexpected

Parallel (inside myMethod):
ans =
     1     1     1   % <- OK, apparently, lab 1 did everything
Parallel (outside class):
ans =
   NaN   NaN   NaN   % <- hmmm...changes did not propagate

这意味着obj.test_value正确分配,并且修改后obj确实正确复制到myMethod's工作区。然而不知何故,这个修改与之前的修改obj是不同的实体obj,因为更改不会传播到更高的堆栈......

将函数更改parallel()为子函数(而不是嵌套函数)并显式传递obj参数,不会影响此结果。

呜呜……这是怎么回事?

标签: matlaboopnestedhandleparfor

解决方案


我可以将问题简化为:

classdef MyObj < matlab.mixin.Copyable

    properties
        test_value = NaN
    end

    methods
        function myMethod(obj)
            obj = obj.copy();
            obj.test_value = rand;
            disp('Inside method:')
            obj.test_value
        end
    end
end

parfor从方程中删除嵌套函数、子函数和。运行上面的代码(经过明显更改后)会导致:

Inside method:
ans =
     4.8089e-01   % <- obj re-initialized OK
Outside class:
ans =
     NaN          % <- but this does NOT modify the handle class!

这意味着这是我不知道的语言“功能”!显然,方法可以修改现有对象,但不能完全重新定义它们。我将尝试在文档中挖掘更多信息,但这带来了一个全新的问题:如何将所做的更改复制parfor回对象中?

TL;博士:

function myMethod(obj)
%   ↓ new object   ↓ old object
    obj       =    obj.copy();

克里斯是对的。尽管新旧物体名称相同,但它们是不同的事物——它们相互影射。对新版本所做的任何更改obj都不会修改旧版本obj


推荐阅读