Delphi 2010 - Thread error: The handle is invalid (6)
Delphi 2010 - Thread error: The handle is invalid (6)
我创建了一个线程来检查端口是打开还是关闭。 运行 753 次后我得到 "Thread error: The handle is invalid (6)"。我究竟做错了什么?我已经针对 50 个端口对其进行了测试,没有遇到任何问题。现在我尝试检查本地 ip 上的 65536 端口,但在 731 端口之后出现此错误。
谢谢!
线程代码如下:
{==============================START THREAD VERIFICARE PORT PRIN EXTRAGERE NUME HOST,IP,PORT============================================}
function elimina_toate_spatiile_goale(const s: string): string;
var
i, j: Integer;
begin
SetLength(Result, Length(s));
j := 0;
for i := 1 to Length(s) do begin
if not TCharacter.IsWhiteSpace(s[i]) then begin
inc(j);
Result[j] := s[i];
end;
end;
SetLength(Result, j);
end;
function Parse2(str: String; delimiter: Char; param_num: Integer): String;
var
c, x, y : LongInt;
begin
x := 1; // param number that we're currently 'in'
y := 0; // position of previous delimiter
for c := 1 to Length(str) do
if str[c] = delimiter then // this char is a delimiter
begin
if x = param_num then
Break;
inc(x);
y := c;
end;
if x = param_num then
Result := Copy(str, y + 1, c - y - 1)
else
Result := '';
end;
type
TThread_extrage_hostname_ip_si_port = class(TThread)
private
fStringul_ce_trebuie_parsat: string;
fHostname,fIP_de_verificat,fPort_de_verificat,fIP_si_port:string;
REZULTAT_verificare_port:BOOLEAN;
flistbox_porturi_online,flistbox_porturi_offline:Tlistbox;
fmemo_loguri:Tmemo;
procedure semnalizare_port_online_sau_offline;
protected
procedure Execute; override;
public
constructor Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
end;
constructor TThread_extrage_hostname_ip_si_port.Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
begin
inherited Create(False);
freeonterminate:=true;
fStringul_ce_trebuie_parsat:=aStringul_ce_trebuie_parsat;
flistbox_porturi_online:= alistbox_porturi_online;
flistbox_porturi_offline:= alistbox_porturi_offline;
fmemo_loguri:= amemo_loguri;
end;
procedure TThread_extrage_hostname_ip_si_port.Execute;
var
IdTCPClient : TIdTCPClient;
begin
// use fURL, fMethod, and fParam as needed...
REZULTAT_verificare_port := False;
fhostname:=(Parse2(fStringul_ce_trebuie_parsat,'=', 1));
fIP_si_port:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,'=', 2));
fIP_de_verificat:=elimina_toate_spatiile_goale(Parse2(fIP_si_port,':', 1));
fPort_de_verificat:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,':', 2)) ;
if (fIP_de_verificat<>'') and (fport_de_verificat<>'') then begin
try
IdTCPClient := TIdTCPClient.Create(nil);
try
IdTCPClient.Host := fIP_de_verificat;
IdTCPClient.Port := strtoint(fPORT_de_verificat);
IdTCPClient.ConnectTimeout:=5000;
IdTCPClient.Connect;
REZULTAT_verificare_port := True;
finally
IdTCPClient.Free;
end;
except
//Ignore exceptions
end;
Synchronize(semnalizare_port_online_sau_offline);
end;
end;
procedure TThread_extrage_hostname_ip_si_port.semnalizare_port_online_sau_offline;
begin
if REZULTAT_verificare_port=true then
begin
{verific daca in listbox-ul pentru porturi online exista deja elementul testat.
Daca elementul nu exista, il adaug.
Daca exista deja, nu il mai adaug.
}
if fListBox_porturi_online.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
begin
fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => ACTIV');
fListBox_porturi_online.Items.Add(fStringul_ce_trebuie_parsat);
end;
Form2.GroupBox_porturi_online.Caption:='PORTURI ONLINE: '+inttostr(fListBox_porturi_online.Items.Count);
end
else
begin
{verific daca in listbox-ul pentru porturi online exista deja elementul testat.
Daca elementul nu exista, il adaug.
Daca exista deja, nu il mai adaug.
}
if fListBox_porturi_offline.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
begin
fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => OPRIT');
fListBox_porturi_offline.Items.Add(fStringul_ce_trebuie_parsat);
end;
end;
Form2.GroupBox_porturi_offline.Caption:='PORTURI OFFLINE: '+inttostr(fListBox_porturi_offline.Items.Count);
end;
{==============================STOP THREAD EXTRAGERE NUME HOST,IP,PORT============================================}
一个疯狂的猜测。
// ........ CODE ABOVE REMOVED
IdTCPClient.Connect;
REZULTAT_verificare_port := True;
finally
if IdTCPClient.connected then // ADD SOMETHING LIKE THIS SECTION ?
begin
IdTCPClient.IOHandler.InputBuffer.clear;
IdTCPClient.Disconnect;
end;
IdTCPClient.Free;
// ........ CODE BELOW REMOVED
如果这解决了问题,那么问题是使用您正在使用的 APIs,单个进程一次只能打开大约 731 个套接字。
或者您正在连接的系统也对同时打开的套接字总数(它可以接收)有限制?
无论哪种方式,您都会在某处达到系统限制。
...
根据您的评论更新。
线程也有资源限制。您不能创建无限制的线程(就像您不能创建无限制的 TCP 套接字一样)。您能否使用线程安全计数器并确保在达到 100 个线程时停止创建线程,直到前一个线程退出。
- 所以计数器从 0 开始。
- 检查计数器是否小于 100,以允许创建线程。
- 如果100或更多,则等待被唤醒(这是通过退出线程完成的)
- 创建线程时计数器加 1。
- 理想情况下,小于 100 的检查和递增 1 是自动完成的。
- 随着每个线程退出,计数器减 1。这必须可靠地完成,因为线程退出可能有多种方式(正常终止、抛出异常等),您需要捕获它们全部。当计数器递减时,您总是会唤醒任何服务员。
现在您可以像 Java 那样使用基本的 sleep()/wait()/synchronize() 自己创建它。你有一个方法只有 returns 递增(它会根据需要等待,直到它可以递增)和另一个总是递减的方法。
现在您可能会发现您的主线程由于其他原因无法休眠,有些系统需要调用API 来获取dead/exited 线程,才能真正释放线程资源。在这种情况下,您会在资源可用时减一,而不是在线程执行结束时减一。
即使是 100 个线程 运行您的代码也会 运行 非常快,您可能会发现,如果您将线程数限制在内核数和内核数的两倍之间系统,你可以让整个事情变得更快(如果任务是 CPU 绑定的)。
由于 100 个线程在 8 核系统上处于活动状态,效率不如 16 个线程在 8 核系统上处于活动状态。然而,您的大部分处理时间将被等待网络连接延迟所支配。所以16个线程是不够的。
这种模式通常称为信号量(带计数器)。
Google 命中 "delpi semaphore threading" http://edn.embarcadero.com/article/29908
我创建了一个线程来检查端口是打开还是关闭。 运行 753 次后我得到 "Thread error: The handle is invalid (6)"。我究竟做错了什么?我已经针对 50 个端口对其进行了测试,没有遇到任何问题。现在我尝试检查本地 ip 上的 65536 端口,但在 731 端口之后出现此错误。 谢谢!
线程代码如下:
{==============================START THREAD VERIFICARE PORT PRIN EXTRAGERE NUME HOST,IP,PORT============================================}
function elimina_toate_spatiile_goale(const s: string): string;
var
i, j: Integer;
begin
SetLength(Result, Length(s));
j := 0;
for i := 1 to Length(s) do begin
if not TCharacter.IsWhiteSpace(s[i]) then begin
inc(j);
Result[j] := s[i];
end;
end;
SetLength(Result, j);
end;
function Parse2(str: String; delimiter: Char; param_num: Integer): String;
var
c, x, y : LongInt;
begin
x := 1; // param number that we're currently 'in'
y := 0; // position of previous delimiter
for c := 1 to Length(str) do
if str[c] = delimiter then // this char is a delimiter
begin
if x = param_num then
Break;
inc(x);
y := c;
end;
if x = param_num then
Result := Copy(str, y + 1, c - y - 1)
else
Result := '';
end;
type
TThread_extrage_hostname_ip_si_port = class(TThread)
private
fStringul_ce_trebuie_parsat: string;
fHostname,fIP_de_verificat,fPort_de_verificat,fIP_si_port:string;
REZULTAT_verificare_port:BOOLEAN;
flistbox_porturi_online,flistbox_porturi_offline:Tlistbox;
fmemo_loguri:Tmemo;
procedure semnalizare_port_online_sau_offline;
protected
procedure Execute; override;
public
constructor Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
end;
constructor TThread_extrage_hostname_ip_si_port.Create(aStringul_ce_trebuie_parsat: string;alistbox_porturi_online,alistbox_porturi_offline:Tlistbox;amemo_loguri:Tmemo);
begin
inherited Create(False);
freeonterminate:=true;
fStringul_ce_trebuie_parsat:=aStringul_ce_trebuie_parsat;
flistbox_porturi_online:= alistbox_porturi_online;
flistbox_porturi_offline:= alistbox_porturi_offline;
fmemo_loguri:= amemo_loguri;
end;
procedure TThread_extrage_hostname_ip_si_port.Execute;
var
IdTCPClient : TIdTCPClient;
begin
// use fURL, fMethod, and fParam as needed...
REZULTAT_verificare_port := False;
fhostname:=(Parse2(fStringul_ce_trebuie_parsat,'=', 1));
fIP_si_port:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,'=', 2));
fIP_de_verificat:=elimina_toate_spatiile_goale(Parse2(fIP_si_port,':', 1));
fPort_de_verificat:=elimina_toate_spatiile_goale(Parse2(fStringul_ce_trebuie_parsat,':', 2)) ;
if (fIP_de_verificat<>'') and (fport_de_verificat<>'') then begin
try
IdTCPClient := TIdTCPClient.Create(nil);
try
IdTCPClient.Host := fIP_de_verificat;
IdTCPClient.Port := strtoint(fPORT_de_verificat);
IdTCPClient.ConnectTimeout:=5000;
IdTCPClient.Connect;
REZULTAT_verificare_port := True;
finally
IdTCPClient.Free;
end;
except
//Ignore exceptions
end;
Synchronize(semnalizare_port_online_sau_offline);
end;
end;
procedure TThread_extrage_hostname_ip_si_port.semnalizare_port_online_sau_offline;
begin
if REZULTAT_verificare_port=true then
begin
{verific daca in listbox-ul pentru porturi online exista deja elementul testat.
Daca elementul nu exista, il adaug.
Daca exista deja, nu il mai adaug.
}
if fListBox_porturi_online.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
begin
fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => ACTIV');
fListBox_porturi_online.Items.Add(fStringul_ce_trebuie_parsat);
end;
Form2.GroupBox_porturi_online.Caption:='PORTURI ONLINE: '+inttostr(fListBox_porturi_online.Items.Count);
end
else
begin
{verific daca in listbox-ul pentru porturi online exista deja elementul testat.
Daca elementul nu exista, il adaug.
Daca exista deja, nu il mai adaug.
}
if fListBox_porturi_offline.Items.IndexOf(fStringul_ce_trebuie_parsat) = -1 then
begin
fmemo_loguri.lines.add(datetimetostr(now)+' - '+fStringul_ce_trebuie_parsat +' => OPRIT');
fListBox_porturi_offline.Items.Add(fStringul_ce_trebuie_parsat);
end;
end;
Form2.GroupBox_porturi_offline.Caption:='PORTURI OFFLINE: '+inttostr(fListBox_porturi_offline.Items.Count);
end;
{==============================STOP THREAD EXTRAGERE NUME HOST,IP,PORT============================================}
一个疯狂的猜测。
// ........ CODE ABOVE REMOVED
IdTCPClient.Connect;
REZULTAT_verificare_port := True;
finally
if IdTCPClient.connected then // ADD SOMETHING LIKE THIS SECTION ?
begin
IdTCPClient.IOHandler.InputBuffer.clear;
IdTCPClient.Disconnect;
end;
IdTCPClient.Free;
// ........ CODE BELOW REMOVED
如果这解决了问题,那么问题是使用您正在使用的 APIs,单个进程一次只能打开大约 731 个套接字。
或者您正在连接的系统也对同时打开的套接字总数(它可以接收)有限制?
无论哪种方式,您都会在某处达到系统限制。
...
根据您的评论更新。
线程也有资源限制。您不能创建无限制的线程(就像您不能创建无限制的 TCP 套接字一样)。您能否使用线程安全计数器并确保在达到 100 个线程时停止创建线程,直到前一个线程退出。
- 所以计数器从 0 开始。
- 检查计数器是否小于 100,以允许创建线程。
- 如果100或更多,则等待被唤醒(这是通过退出线程完成的)
- 创建线程时计数器加 1。
- 理想情况下,小于 100 的检查和递增 1 是自动完成的。
- 随着每个线程退出,计数器减 1。这必须可靠地完成,因为线程退出可能有多种方式(正常终止、抛出异常等),您需要捕获它们全部。当计数器递减时,您总是会唤醒任何服务员。
现在您可以像 Java 那样使用基本的 sleep()/wait()/synchronize() 自己创建它。你有一个方法只有 returns 递增(它会根据需要等待,直到它可以递增)和另一个总是递减的方法。
现在您可能会发现您的主线程由于其他原因无法休眠,有些系统需要调用API 来获取dead/exited 线程,才能真正释放线程资源。在这种情况下,您会在资源可用时减一,而不是在线程执行结束时减一。
即使是 100 个线程 运行您的代码也会 运行 非常快,您可能会发现,如果您将线程数限制在内核数和内核数的两倍之间系统,你可以让整个事情变得更快(如果任务是 CPU 绑定的)。
由于 100 个线程在 8 核系统上处于活动状态,效率不如 16 个线程在 8 核系统上处于活动状态。然而,您的大部分处理时间将被等待网络连接延迟所支配。所以16个线程是不够的。
这种模式通常称为信号量(带计数器)。 Google 命中 "delpi semaphore threading" http://edn.embarcadero.com/article/29908