Delphi无法使用对象引用执行Task中的过程

时间:2018-09-05 14:34:53

标签: delphi delphi-10.2-tokyo

我有一个简单的类,具有以下接口实现。

注意TPolyBase是抽象类,TPolyResultarray of double;看到他们的代码并不重要,在这里也无关紧要。

//INTERFACE

type
  TPolynomialList = class
    strict private
      FPolynomialList: TObjectList<TPolyBase>;
      FResult: TList<TPolyResult>;
      FCanGet: boolean;
      function GetResult: TList<TPolyResult>;
      procedure DoSolve;
    public
      constructor Create(PolynomialList: TObjectList<TPolyBase>);
      destructor Destroy; override;

      procedure SolvePolynomials(CompletionHandler: TProc);
      property Solutions: TList<TPolyResult> read GetResult;
  end;

//IMPLEMENTATION

constructor TPolynomialList.Create(PolynomialList: TObjectList<TPolyBase>);
begin
  FPolynomialList := PolynomialList;
  FResult := TList<TPolyResult>.Create;
  FCanGet := false;
end;

destructor TPolynomialList.Destroy;
begin
  FResult.Free;
  inherited;
end;

procedure TPolynomialList.DoSolve;
var
  i: integer;
begin
  for i := 0 to FPolynomialList.Count - 1 do
    FResult.Add(FPolynomialList[i].GetSolutions);

  FCanGet := true;
end;

function TPolynomialList.GetResult: TList<TPolyResult>;
begin
  if FCanGet = false then
    raise TEquationError.Create('You must solve the equation first!');

  Result := FResult;
end;

procedure TPolynomialList.SolvePolynomials(CompletionHandler: TProc);
begin
    TTask.Run(procedure
              var
                ex: TObject;
              begin
                try
                  DoSolve;
                  TThread.Synchronize(nil, procedure
                                           begin
                                             CompletionHandler;
                                           end);
                except
                  on E: Exception do
                    begin
                      ex := AcquireExceptionObject;
                      TThread.Synchronize(nil, procedure
                                           begin
                                             Writeln( (ex as Exception).Message );  
                                           end);
                    end;
                end;
              end);
end;

该类将一个对象列表作为输入,并且它具有一个内部重要字段FResult,该字段将结果提供给用户。只有方法SolvePolynomials完成工作后,才能从吸气剂访问它。


问题出在SolvePolynomials中。我显示的代码使用了一个任务,因为对象列表的大小可能很大,并且我不想冻结UI。为什么我总是在任务代码中遇到访问冲突?

请注意,以下代码可以正常工作,但这不是我想要的,因为如果我输入15000,程序将冻结几秒钟。

procedure TPolynomialList.SolvePolynomials(CompletionHandler: TProc);
begin
  DoSolve;
  CompletionHandler;
end;

可能是FPolynomialList变量吗?如果您查看我的课程,那么“从外面拿走”的唯一内容就是TObjectList<TPolyBase>,因为在构造函数中,我只是简单地引用了该引用(我希望避免复制ok 15k项)。所有其他变量都没有共享。

我在许多读过的书中都看到了“ Delphi High Performance”之类的书,它很好地执行了一个调用内部“ slow”方法的任务,但在这种情况下,可能会有那些引用弄乱了某些东西。任何想法?


这是我用作测试的代码:

var
 a: TObjectList<TPolyBase>;
 i, j: integer;
 f: TPolynomialList;
 s: string;

 function GetRandom: integer;
 begin
   Result := (Random(10) + 1);
 end;

begin
a := TObjectList<TPolyBase>.Create(true);
 try

   for i := 0 to 15000 do
     begin
       a.Add({*Descendant of TPolyBase*})
     end;


   f := TPolynomialList.Create(a);
   try
     f.SolvePolynomials(procedure
                        var
                          i, j: integer;
                        begin    
                          for i := 0 to f.Solutions.Count - 1 do
                            begin
                              for j := Low(f.Solutions[i]) to High(f.Solutions[i]) do
                                Writeln({output the results...})
                            end;  
                        end);
   finally
     f.Free;
   end;

 finally
   a.Free;
 end;

end.

1 个答案:

答案 0 :(得分:5)

您的SolvePolynomials方法将解决方案委托给另一个线程,并在该线程完成其任务之前返回。在该任务线程正在运行时,有必要对其进行操作的所有数据仍处于活动状态。但是,在您的代码中,您将在SolvePolynomials退出后立即释放必要的对象实例-在您的任务仍在运行时,因此会出现错误。

您必须将这些对象的释放移至完成处理程序中。

基本上,简化的代码如下:

type
  TPolynomialList = class
  public
    destructor Destroy; override;
    procedure DoSolve;
    procedure SolvePolynomials(CompletionHandler: TProc);
  end;

destructor TPolynomialList.Destroy;
begin
  Writeln('Destroyed');
  inherited;
end;

procedure TPolynomialList.DoSolve;
begin
  Writeln('Solving');
end;

procedure TPolynomialList.SolvePolynomials(CompletionHandler: TProc);
begin
  TTask.Run(
  procedure
  begin
    try
      DoSolve;
      TThread.Synchronize(nil,
        procedure
        begin
          CompletionHandler;
        end);
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  end);
end;

procedure Test;
var
  f: TPolynomialList;
begin
  f := TPolynomialList.Create;
  try
    f.SolvePolynomials(
      procedure
      begin
        Writeln('Solved');
      end);
  finally
    f.Free;
  end;
end;

如果运行它,输出将是:

Destroyed
Solving
Solved

但是,如果将释放变量移至完成处理程序中,则执行顺序将是正确的。

procedure Test;
var
  f: TPolynomialList;
begin
  f := TPolynomialList.Create;
  f.SolvePolynomials(
    procedure
    begin
      Writeln('Solved');
      f.Free;
    end);
end;

Solving
Solved
Destroyed

对于您的代码,这意味着将a.Freef.Free都移到完成处理程序中。