代码之家  ›  专栏  ›  技术社区  ›  Alberto Miola user831258

Delphi无法在具有对象引用的任务中执行过程

  •  2
  • Alberto Miola user831258  · 技术社区  · 7 年前

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

    注意 : TPolyBase TPolyResult 是一个 array 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方法完成了他的工作时,才能从getter访问它。


    问题出在 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 回复  |  直到 7 年前
        1
  •  5
  •   Dalija Prasnikar    7 年前

    你的 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.Free f.Free 进入完成处理程序。