【问题标题】:Delphi 2010 - Thread error: The handle is invalid (6)Delphi 2010 - 线程错误:句柄无效(6)
【发布时间】:2016-02-20 14:54:10
【问题描述】:

我创建了一个线程来检查端口是打开还是关闭。运行 753 次后,我得到“线程错误:句柄无效 (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============================================}

【问题讨论】:

    标签: multithreading delphi delphi-2010 indy


    【解决方案1】:

    一个疯狂的猜测。

          // ........ 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
    

    如果这样可以解决问题,那么问题是单个进程使用您正在使用的 API 一次只能拥有大约 731 个打开的套接字。

    或者,您正在连接的系统也可能同时限制打开的套接字总数(它可以接收)?

    无论哪种方式,您都会在某个地方遇到系统限制。

    ...

    由于您的评论而更新。

    线程也有资源限制。你不能创建无限的线程(就像你不能创建无限的 TCP 套接字一样)。您能否使用线程安全计数器并确保在达到 100 个线程时停止创建线程,直到前一个线程退出。

    • 所以计数器从 0 开始。
    • 检查计数器是否小于 100,以允许创建线程。
    • 如果 100 或更多,则等待被唤醒(这是通过退出线程完成的)
    • 创建线程时计数器加 1。
    • 理想情况下,检查小于 100 并递增 1 以原子方式完成。
    • 随着每个线程退出,计数器减 1。这必须可靠地完成,因为线程退出可能有多种方式(正常终止、抛出异常等),您需要将它们全部捕获。当计数器递减时,您总是会唤醒所有服务员。

    现在您可以像在 Java 中一样使用基本的 sleep()/wait()/synchronize() 自己创建它。您有一个仅在递增时返回的方法(它将根据需要等待,直到可以递增)和另一个始终递减的方法。

    现在您可能会发现您的主线程由于其他原因无法休眠,某些系统需要调用 API 来获取死/退出线程,然后才能真正释放线程资源。在这种情况下,当资源可用时减一,而不是在线程执行结束时减一。

    即使运行您的代码的 100 个线程也会运行得非常快,您可能会发现,如果您将线程数限制在核心数和系统中核心数的两倍之间,您可以让整个事情变得更快(如果任务受 CPU 限制)。

    由于 8 核系统上有 100 个线程处于活动状态,因此效率不如 8 核系统上的 16 个线程处于活动状态。但是,您的大部分处理时间将由等待网络连接延迟所支配。所以16个线程是不够的。

    这种模式通常称为信号量(带计数器)。 谷歌点击“delpi 信号量线程”http://edn.embarcadero.com/article/29908

    【讨论】:

    • 谢谢。不幸的是,您的代码升级没有解决问题。我认为这可能与我的线程代码有关,但您让我看到了另一个问题:操作系统(win7)可能的网络限制
    • 看来,如果我只同时检查700个端口,就可以了。我没有收到任何错误。所以我要投票给你的答案,因为它是这类问题的完整解决方案。感谢您分享知识!
    猜你喜欢
    • 2013-08-01
    • 2019-10-16
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多