使用TTask.Run并行运行大量作业时出现“内存不足”错误

时间:2015-12-04 15:55:47

标签: delphi

我正在尝试使用Parallel Programming Library中的新System.Threading.pasDelphi

我有大量的工作要使用TTask.Run并行工作。以下是代码段:

var D: TDataSet;
    T: ITask;
    TaskList: TList<ITask>;
    Q: TThreadedQueue<TData>;
begin
  Q := TThreadedQueue<TData>.Create;

  D := Create_Uni_Direction_DataSet;
  D.Open;

  TaskList := TList<ITask>.Create;

  while not D.Eof do begin
    Q.PushItem(GetData(D));
    T := TTask.Run(
           procedure begin
           var A: TData;
           begin
             A := Q.PopItem;
             Process(A);
             ...
           end
         );
    D.Next;
    TaskList.Add(T);
  end;

  TTask.WaitForAll(TaskList.ToArray);
  ...

  TaskList.Free;
  Q.Free;
end;

请注意,为简化说明,代码已经过简化。

代码将在运行时在Win32平台中返回Out of Memory错误。

由于我试图在TaskList中保留大量ITask引用,内存问题显而易见。

我使用TTask.Run代替TParallel的原因有多种原因:

  1. 我无法确定任务的低限和高限。例如:单向数据集
  2. 线程任务更新UI控件,通过TThread.Synchronize报告状态。在TThread.Synchronize中使用TParallel将冻结申请。
  3. 由于我想监控并确保这些任务在继续之前执行,我使用TTask.WaitForAll检查TaskList.ToArray中的项目。

    显然,for循环仍然有效时,应该有许多任务完成执行。不再需要这些已完成的任务进行监控,因此可以从TaskList中删除。

    但是,如果任务完成了它的工作,就不容易决定或得到通知。也许Delphi的PPL中有方法或现成的结构可以解决我不知道的问题。请分享您的想法或解决方案。

2 个答案:

答案 0 :(得分:2)

这似乎是TParallel.For的完美应用:

TParallel.For(1, 50000000,
  procedure(i: Int64)
  begin
  end);

通过这种方式,您可以让RTL同时管理多少个活动线程,并且您不必创建5000万个对象,只需要有数百万个空闲任务。

答案 1 :(得分:0)

我尝试通过对WaitForAllITask100,000TThreadedQueue<TArray<ITask>>执行type TThreadedTasksQueue_Helper = class helper for TThreadedQueue<TArray<ITask>> public function WaitForAll(const aTasks: TArray<ITask>): IFuture<Integer>; end; function TThreadedTasksQueue_Helper.WaitForAll(const aTasks: TArray<ITask>): IFuture<Integer>; begin PushItem(aTasks); Result := TTask.Future<Integer>( function: Integer var A: TArray<ITask>; begin A := PopItem; TTask.WaitForAll(A); Result := Length(A); end ); end; 来解决此问题。

首先,我为WaitForAll定义一个辅助类:

100,000

然后,我修改了冗长的循环,以便在var i: Integer; Q: TThreadedQueue<Integer>; Batches: TList<IFuture<Integer>>; Batch: IFuture<Integer>; WaitForQ: TThreadedQueue<TArray<ITask>>; TaskList: TList<ITask>; begin Q := TThreadedQueue<Integer>.Create; WaitForQ := TThreadedQueue<TArray<ITask>>.Create; Batches := TList<IFuture<Integer>>.Create; TaskList := TList<ITask>.Create; for i := 1 to 50000000 do begin Q.PushItem(i); TaskList.Add( TTask.Run( procedure begin Q.PopItem; end ) ); if i mod 100000 = 0 then begin Batches.Add(WaitForQ.WaitForAll(TaskList.ToArray)); TaskList.Clear; end; end; Batches.Add(WaitForQ.WaitForAll(TaskList.ToArray)); TaskList.Clear; for Batch in Batches do Batch.Value; Q.Free; WaitForQ.Free; TaskList.Free; Batches.Free; end; 批次中为ITask执行 <?= $this->Form->create($news,array('type'=>'file')) ?> <div class="col-md-12"> <?php echo $this->Form->input('newsImage',['type'=>'file']); echo $this->Form->input('title',['class'=>'form-control']); echo $this->Form->input('news'); ?> </div> <?= $this->Form->end() ?>

 if ($this->request->is('post')) {

                $target_dir = "img/news/";
                $target_file = $target_dir . basename($_FILES["newsImage"]["name"]);

                $fNAME   = $_FILES["newsImage"]["name"];
                $TMPNAME = $_FILES['newsImage']['tmp_name'];

                move_uploaded_file($_FILES["newsImage"]["tmp_name"], $target_file);


                $this->request->data['News']['newsImage']=$fNAME;

                $news = $this->News->patchEntity($news, $this->request->data);
                if ($this->News->save($news)) {
                    $this->Flash->success(__('The news has been saved.'));
                    //return $this->redirect($this->referer()); 
                    return $this->redirect(['action' => 'index']);
                } else {
                    $this->Flash->error(__('The news could not be saved. Please, try again.'));
                }
}