From my limited knowledge about this subject, the following code should work. But I have not the expected result:
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);
My expected result would be 6 and HOST1, but I got 1 and "" (empty)
Please, what I am missing?
Thanks!
List := Clients.LockList;
try
Clients.Add(Client); // <--- mistake here
finally
Clients.UnlockList;
end;
The idiom is that you lock the list with a call to LockList
and that returns a mutable list. So you need to call Add
on List
.
List := Clients.LockList;
try
List.Add(Client);
finally
Clients.UnlockList;
end;
That said, TThreadList
does offer an Add
method that internally uses LockList
. The reason that your call to that Add
was failing is that you have used the default value of Duplicates
which is dupIgnore
. And you were passing the same memory address each time.
Why was the memory address the same each time? Well, the other mistake you made was to destroy your TClient
objects and refer to them later. I guess the memory manager was re-using the memory that you just deallocated.
You might want to set Duplicates
to dupAccept
. At the very least you need to be aware that it has a potential impact.
This program produces your desired output:
{$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.
Or the loop could be simplified even further:
for I := Low(Hosts) to High(Hosts) do
begin
Client := TClient.Create;
Client.Host := Hosts[I];
Clients.Add(Client);
end;
I neglected to perform any deallocations for the sake of a simpler exposition. Obviously in real code you wouldn't leak the way this code does.
Personally I'm not a fan of this class. Not in this age of generics. You really should be looking at 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.