【问题标题】:Critical section in multithreading for ip scannerip扫描仪多线程中的关键部分
【发布时间】:2014-09-03 10:18:38
【问题描述】:

您好,我对临界区有疑问。我有两个线程scannerThread 和scannerchild 线程。下面提到了Scannerthread 的代码。

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 range 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

但我没有得到它。我的输出就像

    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

是不是因为我没有使用临界区?如果是,我应该在哪里获取和离开临界区,以便首先创建的线程可以先进入临界区,然后再进入下一个线程。

【问题讨论】:

  • 看起来 ScannerChild 正在更新 VCL 控件。您不能在线程中执行此操作。
  • 否定。不得从 GUI 线程以外的线程访问 VCL 控件。您的代码很明显违反了该规则。我的另一点是。
  • 否定。 ScannerChild 类显然派生自 TThread。因此它具有FreeOnTerminate、Resume、Execute。因此,ScannerChild.Execute 中的代码远离 GUI 线程执行。你最好提供一个完整的程序,适当地减少到最小的样本。还有许多其他问题。
  • 实际上谁启动线程并不重要,重要的是代码在哪个线程上下文中运行。要临时切换回 MainThread 上下文,请使用 Synchronize 或 Queue。
  • 您为什么希望这些值以任何特定的顺序到达?你了解线程独立运行吗?

标签: multithreading delphi synchronization delphi-7 critical-section


【解决方案1】:

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

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

我确实有很多 cmets:

  1. 将扫描与 UI 分开。这样的设计更简洁,让您可以独立专注于每个方面。实现这一点的方法是将扫描结果存储在非可视结构中,然后安排 GUI 呈现该结构的视图。
  2. 一旦启动线程,就不要在终止线程上保留对 free 的引用,因为该引用可能随时失效。
  3. 不要创建线程来处理单个地址。使用线程池。
  4. 不要睡觉。
  5. 您的代码泄漏IpList。了解如何使用finally。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 关于线程安全字符串列表,Indy 还附带了线程安全字符串列表,来自 IdThreadSafe 单元的TIdThreadSafeStringList 类。
    猜你喜欢
    • 2017-09-13
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多