Delphi的TObject线程列表 - 如何填充?

时间:2014-12-18 20:43:27

标签: multithreading list delphi object tobject

根据我对此主题的有限知识,以下代码应该有效。但我没有预期的结果:

type
  TClient = class(TObject)
    Host: String;
  end;

var
  Clients: TThreadList;

const
  Hosts: Array[0..5] of String = ('HOST1', 'HOST2', 'HOST3', 'HOST4', 'HOST5', 'HOST6');
var
  I: Integer;
  List: TList;
  Client: TClient;
begin
  try
    for I := Low(Hosts) to High(Hosts) do
    begin
      Client := TClient.Create;
      with Client Do
      try
        Host := Hosts[I];
        List := Clients.LockList;
        try
          Clients.Add(Client);
        finally
          Clients.UnlockList;
        end;
      finally
        Client.Free;
      end;
    end;
  except
    on E:Exception Do ShowMessage(E.Message);
  end;

// RESULT TEST
List := Clients.LockList;
try
  I := List.Count;
  S := TClient(List.Items[0]).Host;
finally
  Clients.UnlockList;
end;
ShowMessage(IntToStr(I));
ShowMessage(S);

我的预期结果是6和HOST1,但我得到1和“”(空)

拜托,我缺少什么?

谢谢!

1 个答案:

答案 0 :(得分:6)

List := Clients.LockList;
try
  Clients.Add(Client); // <--- mistake here
finally
  Clients.UnlockList;
end;

成语是你通过调用LockList锁定列表并返回一个可变列表。因此,您需要在Add上致电List

List := Clients.LockList;
try
  List.Add(Client);
finally
  Clients.UnlockList;
end;

尽管如此,TThreadList确实提供了Add内部使用LockList的方法。您对Add的调用失败的原因是您使用的Duplicates默认值为dupIgnore。而且你每次都传递相同的内存地址。

为什么内存地址每次都相同?好吧,你犯的另一个错误是摧毁你的TClient个对象并在以后引用它们。我想内存管理器正在重新使用你刚刚解除分配的内存。

您可能希望将Duplicates设置为dupAccept。至少你需要意识到它有潜在的影响。

该程序产生您想要的输出:

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes;

type
  TClient = class(TObject)
    Host: String;
  end;

const
  Hosts: Array[0..5] of String = ('HOST1', 'HOST2', 'HOST3', 'HOST4', 
    'HOST5', 'HOST6');
var
  I: Integer;
  List: TList;
  Client: TClient;
  Clients: TThreadList;
begin
  Clients := TThreadList.Create;
  Clients.Duplicates := dupAccept;

  for I := Low(Hosts) to High(Hosts) do
  begin
    Client := TClient.Create;
    Client.Host := Hosts[I];
    List := Clients.LockList;
    try
      List.Add(Client);
    finally
      Clients.UnlockList;
    end;
  end;

  List := Clients.LockList;
  try
    Writeln(List.Count);
    Writeln(TClient(List.Items[0]).Host);
  finally
    Clients.UnlockList;
  end;
end.

或者循环可以进一步简化:

for I := Low(Hosts) to High(Hosts) do
begin
  Client := TClient.Create;
  Client.Host := Hosts[I];
  Clients.Add(Client);
end;

为了更简单的阐述,我忽略了执行任何解除分配。显然,在实际代码中,您不会泄漏此代码的方式。

就个人而言,我不是这个班的粉丝。不是在这个泛型的时代。你真的应该看TThreadList<T>

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, Generics.Collections;

type
  TClient = class
    Host: string;
    constructor Create(AHost: string);
  end;

constructor TClient.Create(AHost: string);
begin
  inherited Create;
  Host := AHost;
end;

const
  Hosts: array[0..5] of string = ('HOST1', 'HOST2', 'HOST3', 'HOST4',
    'HOST5', 'HOST6');

var
  Host: string;
  List: TList<TClient>;
  Clients: TThreadList<TClient>;

begin
  Clients := TThreadList<TClient>.Create;
  Clients.Duplicates := dupAccept;

  for Host in Hosts do
    Clients.Add(TClient.Create(Host));

  List := Clients.LockList;
  try
    Writeln(List.Count);
    Writeln(List.First.Host);
  finally
    Clients.UnlockList;
  end;
end.