1

您好,我对关键部分有疑问。我有两个线程scannerThread和scannerchild thread.Code for Scannerthread在下面提到。ip扫描器多线程中的关键部分

procedure ScannerThread.Execute; 
var 
    I: Integer; 
    ScannerCh: Array of ScannerChild; 
    IpList: TStringlist; 
    IPCount: Integer; 
begin 
    IpList:=TStringList.Create; 
    IF GetNumberOfIpsInRange(Ip_From, Ip_To, IpList) then // Function call that returns iplist if TRUE 
    begin 
    Try 
     if Assigned(LvHosts) then // Clear 
     LvHosts.Clear; 
     IPCount := IpList.Count; 
     SetLength(ScannerCh, IPCount); 
     I := 0; 
     repeat 
      while GetTThreadsCount(GetCurrentProcessId) > tcount do // Tcount is threads to create which is given by user  
      Sleep(10); 
      ScannerCh[I]:=ScannerChild.Create(True, IpList[i]); 
      ScannerCh[I].FreeOnTerminate := True; 
      ScannerCh[I].LvHostname := LvHosts; 
      ScannerCh[I].Resume; 
      Inc(I); 
     until I = IPCount; 
     if Assigned(IpList) Then 
     FreeAndNil(IpList); 
    except 
     ShowMessage('Operation Failed'); 
     If Assigned(IpList) Then 
     FreeAndNil(IpList); 
    end; 
    end else 
    ShowMessage('Invalid Range'); 
    repeat 
    Sleep(100); 
    Until GetTThreadsCount(GetCurrentProcessId) = 2; 

end; 

代码Scannerchild如下

procedure ScannerChild.AddToList; 
begin 
    ListItems1 := LVHostName.Items.Add; 
    ListItems1.Caption := IPToScan; 
    ListItems1.SubItems.Add(IPAddrToName(IPToScan)); 
end; 

procedure ScannerChild.AddToList1; 
begin 
    ListItems1:=LVHostName.Items.Add; 
    ListItems1.Caption := IPToScan; 
    ListItems1.SubItems.Add('No host found'); 
end; 

procedure ScannerChild.Execute; 
Var 
    ListItems1 : TListItem; 
    Hostname : String; 
begin  
    Hostname := IPAddrToName(IPToScan); 
    if Hostname <> EmptyStr then 
    begin 
     Synchronize(AddToList); 
    end else 
     synchronize(AddToList1); 
end; 

这里提到我得到输出,但它不是序列。像首先创建的线程不首先显示。如果我把IP地址范围192.168.0.1至192.168.0.10那么我应该得到的ListView串行输出像

192.168.0.1  hostname 
192.168.0.2  hostname 
192.168.0.3  hostname 
. 
. 
192.168.0.10 hostname 

,但我没有得到it.my输出快到像

192.168.0.1 hostname 
    192.168.0.2 hostname 
    192.168.0.4 hostname 
    192.168.0.6 hostname 
    192.168.0.3 hostname 

因此,它是因为我没有使用关键部分?如果是的话,我应该在哪里获得并离开关键部分,以便首先创建的线程可以像这样进入临界区首先和下一个线程。

+5

它看起来像ScannerChild正在更新VCL控件。你不能在一个线程中做到这一点。 – 2014-09-03 10:27:35

+0

事实上,您无法从主线程之外访问GUI控件。除此之外,你的设计是错误的。您确实需要将扫描逻辑与GUI分开。 – 2014-09-03 10:43:31

+0

nope这里有主窗体有这些线程 – 2014-09-03 10:51:35

回答

5

线程并行执行,彼此独立。除非在线程之间强加一些同步或序列化,否则他们完成任务的顺序是不可预知的。

我建议您不要尝试按特定顺序更新UI,因为这样做会妨碍扫描的性能,或使代码更加复杂。

我有一些意见:

  1. 单独从UI的扫描。这样的设计更清晰,并且可以让您专注于每个方面。实现此目的的方法是将扫描结果存储在非可视结构中,然后安排GUI呈现此结构的视图。
  2. 一旦你启动了线程,不要在释放线程时保留free的引用,因为引用可能会在任何时候失效。
  3. 不要创建线程来处理单个地址。使用线程池。
  4. 不要睡觉。
  5. 您的代码泄漏IpList。了解如何使用finally
0

除了其他好的建议,我建议你看看Tilo Eckert的UThreadStringList。代码在这里发布和解释:http://www.swissdelphicenter.ch/torry/showcode.php?id=2167 这是一个线程安全的TStringList,内置关键部分锁。您可以像使用任何TStringlist一样使用它,因此您可以将所有结果填入其中,然后进行排序,然后在所有线程完成时分配回UI。
如果你想实现一个线程池,你可以有一个UThreadStringList充当工作队列,然后另一个充当结果。

+0

关于线程安全字符串列表,Indy还附带了线程安全字符串列表,IdThreadSafe单元中的TIdThreadSafeStringList类。 – TLama 2014-09-03 13:29:24