delphi 如何设计数据大小可变的FIFO队列?

wwwo4jvm  于 2022-11-04  发布在  其他
关注(0)|答案(4)|浏览(186)

我正在处理FIFO队列(简单的一个,就是先推的内容,首先弹出),但我不确定我的设计方式。我将存储在那里的数据类型将提前知道,让我们说,将是相同的,为这个类的每个示例。我想使用TList,其中记录与以下定义将存储(@大卫-这是D2007的版本,所以我没有可用的Generics.Collections:)

type
  PListItem = ^TListItem;
  TListItem = record
    Size: Integer; // size of the data pointed by the following member
    Data: Pointer; // pointer to the target data reserved in memory
  end;

使用这样的实现(我在这里假设一切正常,所以没有使用异常处理)

type
  TListQueue = class
private
  FList: TList;
public
  constructor Create;
  destructor Destroy; override;
  procedure Clear;
  procedure Push(const Value; const Size: Integer);
  procedure Pop(var Value; var Size: Integer);
end;

constructor TListQueue.Create;
begin
  inherited;
  FList := TList.Create;
end;

destructor TListQueue.Destroy;
begin
  Clear;
  FList.Free;
  inherited;
end;

procedure TListQueue.Push(const Value; const Size: Integer);
var ListItem: PListItem;
begin
  New(ListItem);
  ListItem.Size := Size;
  ListItem.Data := AllocMem(Size);
  Move(Value, ListItem.Data^, Size);
  FList.Add(ListItem);
end;

procedure TListQueue.Pop(var Value; var Size: Integer);
var ListItem: PListItem;
begin
  if FList.Count > 0 then
  begin
    ListItem := FList.Items[0];
    Size := ListItem^.Size;
    Move(ListItem.Data^, Value, ListItem.Size);
    FreeMem(ListItem.Data, ListItem.Size);
    Dispose(ListItem);
    FList.Delete(0);
  end;
end;

procedure TListQueue.Clear;
var I: Integer;
    ListItem: PListItem;
begin
  for I := 0 to FList.Count - 1 do
  begin
    ListItem := FList.Items[I];
    FreeMem(ListItem.Data, ListItem.Size);
    Dispose(ListItem);
  end;
  FList.Clear;
end;

我的问题是:
这是一种有效的方法,如何使FIFO队列(如字符串,流,记录等数据类型)的大小从几个字节到大约1 MB(在流的情况下)?
多谢了

px9o7tmv

px9o7tmv1#

我建议使用Contnrs.pas中内置的TQueue和/或TObjectQueue。由于缺少泛型,可以为所使用的每种数据类型派生一个特殊的TQueue。这将在程序的其余部分中给予类型安全,而所有与类型转换和指针相关的东西都捆绑在队列类中。

wyyhbhjk

wyyhbhjk2#

为什么不用途:

type
  PListItem = ^TListItem;
  TListItem = record
    Size: Integer; // size of the data pointed by the following member
    Data: Pointer; // pointer to the target data reserved in memory
    Next: PListItem; // pointer to the next data entry, or nil for the last one.
  end;

您还需要一个var Root: PListItem = nil;,并使用New()和Dispose()分配/取消分配项目。您可能希望添加一个包含列表中最后一个项目的var LastItem: PListItem = nil;,这样您就不必每次添加项目时都遍历整个列表。
虽然与现代的“基于对象的解决方案”相比仍然很原始,但对于FIFO解决方案来说,单个链表仍然非常有效。不太优雅,但嘿,它足够好用了。如果你想要更优雅,围绕这一切构建一个类吧!

ee7vknir

ee7vknir3#

我将使用内存流和TObjectQueue(如Uwe所建议的)。

type
  TListQueue = class
  private
    FList: TObjectQueue;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Push(const Value; const Size: Integer);
    procedure Pop(var Value; var Size: Integer);
  end;

implementation

constructor TListQueue.Create;
begin
  inherited;
  FList := TObjectQueue.Create;
end;

destructor TListQueue.Destroy;
begin
  while FList.Count > 0 do
    TMemoryStream(FList.Pop).Free;
  FreeAndNil(FList);
  inherited;
end;

procedure TListQueue.Push(const Value; const Size: Integer);
var
  LStream: TMemoryStream;
begin
  LStream := TMemoryStream.Create;
  LStream.Write(Value, Size);
  FList.Push(LStream);
end;

procedure TListQueue.Pop(var Value; var Size: Integer);
var
  LStream: TMemoryStream;
begin
  if FList.Count > 0 then
  begin
    LStream := TMemoryStream(FList.Pop);
    Size := LStream.Size;
    LStream.Position := 0;
    LStream.Read(Value, Size);
    LStream.Free;
  end;
end;
w46czmvw

w46czmvw4#

最后,我选择了TStringList来实现这个目标,它看起来工作得很好,但是它不是为多线程设计的。

// First in first out/Last in first out (FIFO by default)
  TStringQueueType = (FIFO, LIFO);
  TStringQueue = class
    private
      QList: TStringList;
      QType: TStringQueueType;
    public
      constructor Create(); overload;
      procedure Push(str: String);
      function Pop(): String;
      procedure Clear();
  end;

  constructor TStringQueue.Create();
  begin
    inherited Create();
    QList := TStringList.Create();
    QType := FIFO;
  end;
  procedure TStringQueue.Push(str: String);
  begin
    QList.Add(str);
  end;
  function TStringQueue.Pop(): String;
  begin
    if QList.Count > 0 then
    begin
      case QType of
        FIFO:
        begin
          Result := QList.Strings[0];
          QList.Delete(0);
        end;
        LIFO:
        begin
          Result := QList.Strings[QList.Count - 1];
          QList.Delete(QList.Count - 1);
        end;
        else
          Result := '';
      end;
    end;
  end;
  procedure TStringQueue.Clear();
  begin
    QList.Clear();
  end;

相关问题