3

TClientDataset を使用してメモリ内データセットを作成し、受信バッファとして使用しています。データを追加するのは素晴らしいことですが、処理を開始したら、データセットから行を削除できるようにしたいと考えています。delete を呼び出すと、行/インデックスには引き続きアクセスできますが、有効な情報は含まれていません。

このバッファーを処理しているときに、エントリが実際に削除されるとは限らないため、これは少し難しくなります。最初のエントリからバッファのスキャンを開始して空のアイテムをスキップしたくないので、データセットからアイテムを永久に「削除」するより良い方法はありますか? 私の考えは、行を削除しても空のレコードが残らない実際の SQL テーブルのように機能するはずだということでした。

これを達成する最善の方法は何ですか、または間違ったコンポーネントを完全に使用していますか?

4

3 に答える 3

1

コードに問題があります。数日後にマルチスレッド環境で TClientDataSet に直面するため、この場合のテスト アプリケーションを用意しました。テスト ケース アプリケーションでこの問題が発生していない (Delphi 2010 Update 5)

このコードは、数日後に自分のブログでも公開します...今のところ、あなたに渡しました。

DFM ファイル:

object Form2: TForm2
  Left = 0
  Top = 0
  Caption = 'Form2'
  ClientHeight = 337
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  PixelsPerInch = 96
  TextHeight = 13
  object Memo1: TMemo
    Left = 8
    Top = 8
    Width = 257
    Height = 321
    Lines.Strings = (
      'Memo1')
    TabOrder = 0
  end
  object Button1: TButton
    Left = 271
    Top = 8
    Width = 170
    Height = 25
    Caption = 'Start'
    TabOrder = 1
    OnClick = Button1Click
  end
  object cdsTest: TClientDataSet
    Aggregates = <>
    Params = <>
    Left = 584
    Top = 32
    object cdsTestNumber: TIntegerField
      FieldName = 'Number'
    end
  end
  object tToMemo: TTimer
    Enabled = False
    Interval = 500
    OnTimer = tToMemoTimer
    Left = 376
    Top = 144
  end
end

pas ファイル:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, DBClient, SyncObjs, ExtCtrls;

type
  TWriterThread = class(TThread)
  private
    FDataSet: TClientDataSet;
    //FWriteLock: TMultiReadExclusiveWriteSynchronizer;
    FLock: TCriticalSection;
  public
    constructor Create(ADataSet: TClientDataSet; ALock: TCriticalSection);
    procedure Execute; override;
  end;

  TDeleterThread = class(TThread)
  private
    FDataSet: TClientDataSet;
    //FWriteLock: TMultiReadExclusiveWriteSynchronizer;
    FLock: TCriticalSection;
  public
    constructor Create(ADataSet: TClientDataSet; ALock: TCriticalSection);
    procedure Execute; override;
  end;

  TForm2 = class(TForm)
    cdsTest: TClientDataSet;
    Memo1: TMemo;
    cdsTestNumber: TIntegerField;
    Button1: TButton;
    tToMemo: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure tToMemoTimer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    FLock: TCriticalSection;
    FWriterThread: TWriterThread;
    FDeleterThread: TDeleterThread;
    procedure cdsToMemo;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  Button1.Enabled := False;
  cdsTest.CreateDataSet;
  cdsTest.LogChanges := False;
  FLock := TCriticalSection.Create;
  tToMemo.Enabled := True;
  FWriterThread := TWriterThread.Create(cdsTest, FLock);
  FDeleterThread := TDeleterThread.Create(cdsTest, FLock);
end;

{ TWriterThread }

constructor TWriterThread.Create(ADataSet: TClientDataSet;
  ALock: TCriticalSection);
begin
  inherited Create(False);
  FDataSet := ADataSet;
  FLock := ALock;
end;

procedure TWriterThread.Execute;
var
  I: Integer;
begin
  inherited;
  I := 0;
  while not Terminated do
  begin
    FLock.Enter;
    try
      Inc(I);
      FDataSet.AppendRecord([I]);
    finally
      FLock.Leave;
    end;
    Sleep(500);  //a new record aproximately each half second
  end;
end;

{ TDeleterThread }

constructor TDeleterThread.Create(ADataSet: TClientDataSet;
  ALock: TCriticalSection);
begin
  inherited Create(False);
  FDataSet := ADataSet;
  FLock := ALock;
end;

procedure TDeleterThread.Execute;
const
  MaxRecords = 100;
var
  ProcessedRecords: Integer;
begin
  inherited;
  while not Terminated do
  begin
    Sleep(3000);  //delete records aproximately every 3 seconds
    FLock.Enter;
    try
      FDataSet.First;
      ProcessedRecords := 0;
      while (not FDataSet.Eof) and (ProcessedRecords < MaxRecords) do
      begin
        Inc(ProcessedRecords);
        if Odd(FDataSet.Fields[0].AsInteger) then
          FDataSet.Delete
        else
          FDataSet.Next;
      end;
    finally
      FLock.Leave;
    end;
  end;
end;

procedure TForm2.cdsToMemo;
begin
  FLock.Enter;
  try
    Memo1.Lines.BeginUpdate;
    try
      Memo1.Lines.Clear;
      cdsTest.First;
      while not cdsTest.Eof do
      begin
        Memo1.Lines.Add(cdsTestNumber.AsString);
        cdsTest.Next;
      end;
    finally
      Memo1.Lines.EndUpdate;
    end;
  finally
    FLock.Leave;
  end;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  tToMemo.Enabled := False;
  if cdsTest.Active then
  begin
    FDeleterThread.Terminate;
    FDeleterThread.WaitFor;
    FWriterThread.Terminate;
    FWriterThread.WaitFor;
  end;
end;

procedure TForm2.tToMemoTimer(Sender: TObject);
begin
  tToMemo.Enabled := False;
  cdsToMemo;
  tToMemo.Enabled := True;
end;

end.

あなたはマルチスレッドに精通しているように見えるので、これ以上の説明は投稿しません。ご不明な点がございましたら、お気軽に質問にコメントしてください。

1 つだけ... TMultiReadExclusiveWriteSynchronizer を使用して同時実行を改善することを計画していましたが、ReadAccess を WriteAccess に昇格させた経験がないため、CriticalSection を使用して、今すぐ調査するのに必要な時間を回避しました。

于 2010-09-10T03:09:11.150 に答える
1

クライアント側の変更が切断されたセッション (「ブリーフケース モデル」) で行われた場合でも、クライアント側の変更をリモート サーバーに送信できるように設計されているため、既定では、クライアント データセットは変更の「ログ」を保持します。通常、このログはリモート データベースに変更を適用すると「クリア」され、その他の変更は「ローカル」コピーにマージされます。LogChanges が不要で直接変更したい場合は、LogChanges を False に設定します。

于 2010-09-09T13:46:51.757 に答える
0

あなたのコードに関するいくつかのコメント。

  1. データセットをループするために異常な方法を使用しています (カウンターを使用し、まだ next を使用しています)。

  2. 削除するときの私の好みの方向は、最初から最後までです。

  3. 削除後にデータセットを投稿しません。

私の提案は、次のようなことを試すことです。

MyDataSet.RecNo:= 99
while not MyDataSet.Bof do
begin
  fD1 := MyDataset.FieldByName('Field1').AsInteger;
  fD2 := MyDataset.FieldByName('Field2').AsInteger;
  fD3 := MyDataset.FieldByName('Field3').AsInteger;

  if someCondition then
    MyDataset.Delete;

  MyDataSet.Post;

  MyDataset.Previous;
end;
于 2010-09-09T15:00:40.823 に答える