Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Database 数据库记录处理_Database_Delphi_Delphi Xe2_Accuracerdb - Fatal编程技术网

Database 数据库记录处理

Database 数据库记录处理,database,delphi,delphi-xe2,accuracerdb,Database,Delphi,Delphi Xe2,Accuracerdb,如何在多个线程之间分配记录的工作负载?例如,Accuracer DB有169条记录,有7个线程 因为我可以把记录的数量分成几个范围,让每个线程处理这个范围。但如果用户删除或添加记录,它将无法正常工作。您可以使用它并行处理数据库中的记录,而无需太多麻烦 我使用管道抽象编写了一个示例。管道施工分为三个阶段: 第一阶段从数据库读取数据,创建容器对象的实例,以表示管道下一阶段的数据 第二阶段处理传入的数据 使用过程调用DoSomethingWith,该过程只会浪费大约100毫秒的时间来模拟数据的处理

如何在多个线程之间分配记录的工作负载?例如,Accuracer DB有169条记录,有7个线程

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

您可以使用它并行处理数据库中的记录,而无需太多麻烦

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

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

    • 使用过程调用
      DoSomethingWith,该过程只会浪费大约100毫秒的时间来模拟数据的处理
    • 释放容器实例的内存
    • 然后将文本值
      1
      添加到输出队列,以通知最后一个阶段已处理另一条记录
    此阶段配置为在7个线程中并行运行

  • 最后一个阶段只计算上一个阶段已完成的记录数
  • 该示例是一个控制台应用程序,允许您只需复制/粘贴即可看到它在您的计算机中实时工作

    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.
    
    IMHO,这样做的主要优点是:

    • 您有一个灵活的机制在多个工作人员之间分配作业。如果某些记录需要更多的时间来处理,那么库会处理好这种情况,您可以合理地期望在更短的时间内完成全部工作
    • 从数据库中读取完第一条记录后,第一个处理线程就开始了
    • 如果必须等待基表中更多的传入记录,则可以轻松地进行调整。在阶段过程中的代码结束之前,阶段的输出队列不会标记为已完成。如果在某个时候没有更多的工作要做,所有即将到来的阶段都会阻塞等待更多的数据处理
    • 您只需更改参数值即可更改工作线程的数量
    您的问题是“如何在线程中处理它?”还是“如何分配工作以便每个线程都有相同的工作要做?”,或者两者都有?.你是说numberOfRecordsPerThread=totalRecords/7不起作用,因为可能会有新的记录进来?@JachGrate如何分配工作以使每个线程都有相同的工作负载:)谢谢你提醒我我将进行编辑..好吧,在你为线程分配初始记录数之后,如果您有更多的记录要处理,您可以将它们随机分配给线程。但我要做的是使用一个dispatcher类,当线程处理的记录用完时,让线程请求记录。如果记录处理得很快,您可以给线程批处理2或3或5或10条记录。你的问题很有趣,但很笼统,所以很难写出具体的答案。我的一般建议是看看哪个IMHO是最好的选择,你可以不费吹灰之力地写这篇文章。如果你已经做了一些事情,最好是展示你必须得到的有用的答案。一个问题是这个问题会阻止GUI的主线程吗?因为OTL中的示例阻止了GUI.:)在本例中,我需要阻止它,因为应用程序以另一种方式在实际工作开始之前结束。但是您可以更改它,当您知道自己在做什么时,更改是微不足道的。提示:
    Run
    方法会立即返回。数据是直接从数据集读取还是复制的?嘿,这里有(注释的)代码可以查看它!。你为什么问这样的问题?你理解代码吗?你读过注释了吗?