数据库记录处理

时间:2013-03-10 06:02:56

标签: database delphi delphi-xe2 accuracerdb

如何在多个线程之间拆分记录的工作量,特别是具有169条记录和7个线程的Accuracer DB。

因为我可以只分割范围中的记录数,让每个线程处理范围。但是如果用户删除或添加记录,它将无法正常工作。

1 个答案:

答案 0 :(得分:3)

您可以使用OmniThreadLibrary并行处理来自数据库的记录,而不会有太多麻烦。

我使用 Pipeline 抽象编写了一个示例。管道分为3个阶段:

  1. 第一阶段从数据库读取数据,创建容器对象的实例,以表示管道下一阶段的数据。
  2. 第二阶段处理传入的数据。

    • 调用DoSomethingWith过程,该过程只会浪费大约100毫秒。模拟数据的处理
    • 释放容器实例的内存。
    • 然后将文字值1添加到输出队列,以通知最后阶段已处理另一条记录。

    此阶段配置为在7个线程中并行运行。

  3. 最后一个阶段计算从前一阶段完成的记录数
  4. 该示例是一个控制台应用程序,允许您复制/粘贴以查看它在您的计算机中正常工作。

    program Project1;
    
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    
    uses
      System.SysUtils,
      OtlCommon,
      OtlCollections,
      OtlParallel,
      System.Diagnostics,
      DB, DBClient;
    
    type
      //auxiliar container, used to copy the database data
      //to avoid synchronization. remember TDataSet "current record"
      //may cause conflicts if changed from different threads.
      TContainer = class
      private
        FName: string;
        FID: Int64;
      public
        property ID: Int64 read FID write FID;
        property Name: string read FName write FName;
      end;
    
    //does nothing, but wastes around 100ms. "processing" each record
    procedure DoSomethingWith(const AValue: TContainer);
    begin
      Sleep(100);
    end;
    
    //creates a DataSet on the fly with a random number of records
    function CreateDataSet: TClientDataSet;
    var
      I: Integer;
    begin
      Result := TClientDataSet.Create(nil);
      with Result.FieldDefs.AddFieldDef do
      begin
        Name := 'ID';
        DataType := ftLargeint;
      end;
      with Result.FieldDefs.AddFieldDef do
      begin
        Name := 'NAME';
        DataType := ftString;
      end;
      Result.CreateDataSet;
      for I := 1 to Random(1000) do
        Result.InsertRecord([I, 'Test']);
    end;
    
    var
      RecordsProcessed: Integer;
      SW: TStopwatch;
      Data: TDataSet;
    begin
      IsMultiThread := True;
      Randomize;
      Writeln('wait while processing...');
      SW := TStopwatch.Create;
      SW.Start;
      try
        Data := CreateDataSet;
        try
          RecordsProcessed := Parallel.Pipeline
            .Stage(
              procedure (const Input, Output: IOmniBlockingCollection)
              var
                RecData: TContainer;
              begin
                Data.First;
                while not Data.Eof do
                begin
                  RecData := TContainer.Create;
                  RecData.ID := Data.Fields[0].AsLargeInt;
                  RecData.Name := Data.Fields[1].AsString;
                  Output.Add(RecData);
                  Data.Next;
                end;
              end)
            .Stage(
              procedure (const Input: TOmniValue; var Output: TOmniValue)
              begin
                //process the real thing here
                DoSomethingWith(Input);
                Input.AsObject.Free;
                Output := 1; //another record
              end)
            .NumTasks(7) //this stage is processed by 7 parallel tasks
            .Stage(
               procedure (const Input, Output: IOmniBlockingCollection)
               var
                 Recs: Integer;
                 Value: TOmniValue;
               begin
                 Recs := 0;
                 for Value in Input do
                   Inc(Recs, Value);
                 Output.Add(Recs);
               end)
            .Run.Output.Next;
          SW.Stop;
          Writeln(RecordsProcessed, ' records processed in ', SW.ElapsedMilliseconds, 'ms.');
          Writeln('Avg. ', (SW.ElapsedMilliseconds/RecordsProcessed):0:3, 'ms./record');
        finally
          Data.Free;
        end;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
      readln;
    end.
    

    这样做的主要优点,恕我直言,是:

    • 您可以灵活地在多个工作人员之间分配作业。如果某些记录需要更多时间来处理,那么图书馆会处理这种情况,您可以合理地期望在更短的时间内完成整个工作。
    • 一旦您从数据库中读取第一条记录,就会首先处理线程启动。
    • 如果您必须等待基表中的更多传入记录,您可以轻松地进行调整。在阶段过程中的代码结束之前,阶段的输出队列将不会标记为已完成。如果在某个时候没有更多的工作要做,所有即将到来的阶段只会阻止等待更多的数据处理。
    • 只需更改参数值即可更改工作线程数!