一些不确定的线程正在创建而不是终止

时间:2014-09-12 06:10:20

标签: multithreading delphi

我对多线程应用程序(ip扫描程序)有疑问。当我把192.168.0.1这样的大ip范围放到192.168.5.1并且线程限制为99时。因此,当我运行我的应用程序时,应该一次运行101个线程(99个线程(ScannerChild)+主线程+ Scannerthread)并且当扫描时完成了99个scannerchild和1个scannethreads将被终止,那个时间只有1个线程运行(主线程)。但有时线程计数将达到102,并且在扫描线程计数未达到1之后,它在任务管理器中将threadcount显示为2。问题是什么?

Scannerthread的代码 /

/Creating constructor of scannerthread
Constructor ScannerThread.Create(CreateSuspended: Boolean );
Begin
    Inherited Create(CreateSuspended);
  Freeonterminate:= true;   //Freeonterminate is true
End;

{ScannerThread Thread }
procedure ScannerThread.Execute;
var
    I  :    integer;
    ScannerCh : array  of ScannerChild;  //array of ScannerChild
    IpList : TStringlist; //Iplist as tstringlist
    IPs: Integer;   //ipcount is count of iplist
Begin
  ScannerchCount:=0;  //Initialising scannerchcount as 0
  IpList:=TStringList.Create;//creating stringlist
  IF GetNumberOfIpsInRange(Ip_From, Ip_To, IpList)  Then    //Function call that returns iplist if TRUE
  Begin
    Try
      IF Assigned(LvHosts) Then        //Clearing LvHosts  field
        LvHosts.Clear;
      IPs := IpList.Count; //Ipcount is given value of iplists count
      SetLength(ScannerCh, IPs);  //Setting length of scannerch as ipcount

      I:=0;
        Repeat
          While ScannerChcount > tcount-1 do  //Checking if  is greater than tcount(thread input) by user
            Sleep(30);
          ScannerCh[I]:=ScannerChild.Create(True, IpList[i]);
          ScannerCh[I].FreeOnTerminate:=True;
          ScannerCh[I].OnTerminate:= ScanchildTerminated; // Event scanchildterminated occurs on termination of Scannerch thread
          ScannerCh[I].LvHostname := LvHosts;   //Lhostname is private listview of scannechild
          ScannerCh[I].Resume;
          ScannerChCount:=Scannerchcount+1;   //Incrementing scannerchcounts
          I:=I+1;
          Sleep(20);   //Sleep after each thread is created so that threads will enter critical section properly
       until I = IPs;

       Scannerch:=nil;
      If Assigned(IpList) Then   //Free iplist
        FreeAndNil(IpList);

    Except
      On E: Exception do
       Begin
        ShowMessage('Invalid operation :' + E.Message); //Showexception message

      If Assigned(IpList) Then          //Free iplist
         FreeAndNil(IpList);
      end;   
    End;
  End
  Else
  Begin
   Ipscan.lbResult.caption:='Invalid Ip Range';
   Exit;
  End;
  Repeat                                                                      //Main Thread Waiting For Ip scan Threads to finish
   Sleep(100);
  until ScannerChCount = 0;

End;

Scannerchild代码

Constructor ScannerChild.Create(CreateSuspended: Boolean; IP: String);
Begin
    Inherited Create(CreateSuspended);
  //FCriticalsection := TCriticalSection.create;  //Creating critical section
  IPToScan:=IP;
End;



//Execution procedure for scannerchild
procedure ScannerChild.Execute;
Var
 MainOutput : TListItem;//Listitem variable for adding listitems
 Hostname   : String; //Hostname is declared as string
Begin
 Try
  MainOutput:=LvHostname.Items.Add; //Adding items to mainoutput
  MainOutput.Caption:=IPToScan;
  Hostname := IPAddrToName(IPToScan);
  If Hostname <> EmptyStr Then
  Begin
    MainOutput.SubItems.Add(IPAddrToName(IPToScan));  //Displaying output
  End
  Else
    Mainoutput.subitems.add('No host');
  Finally
  End;

End;

//this event get called when scannerch thread terminates
procedure Scannerthread.ScanchildTerminated( Sender : TObject );
Begin
  ScannerChCount:=ScannerchCount-1; // Decrementing scannerchcount

End;

1 个答案:

答案 0 :(得分:4)

这里有很多问题。我会给你一些一般的建议,并尝试回答你的问题。

正如我们之前所说,您在主线程之外访问GUI是错误的。无需再次讨论,请回顾之前的问题。

您的线程设计很差。如果您要问一个关于我们的高级问题,我们可以帮助您解决问题。如果您提出一个允许我演示简单线程池的问题,我会很高兴。

除了线程设计的问题之外,您还没有关注点。没有模块化。线程和任务以及GUI代码都是相互混合的。您需要将关注点分开,以使代码可维护且考虑周全。如果您只问我们如何设计您的程序而不是修复您的弱设计中的错误,我们可以帮助您。

Sleep的所有调用和投票都是这种糟糕设计的症状。应该没有睡觉。

你的代码有太多评论,难以阅读。没有必要评论像i := i+1这样的陈述。这种影响是不言而喻的。

您需要学习如何调试线程代码。交互式调试器不是那么有用。它会干扰线程执行的时间。使用跟踪日志记录来调试此类问题。在你学会如何做到这一点之前,你不能指望取得进步。我再说一遍,学习如何调试至关重要。

关于您询问的问题,您在ScannerChCount变量上进行了数据竞争。所以线程可能正确终止,但你错误地计算它们。

使用InterlockedIncrementInterlockedDecrement以线程安全的方式对其进行修改。这是在子终止代码和控制器线程中。

您可能认为这不是必需的,因为递减计数器的ScanChildTerminated是一个OnTerminate事件,因此由主线程执行。但是,在主线程中不执行递增计数器的控制器线程代码。

如果您还不知道数据竞争是什么,那么您很快就会启动多线程编程。而不是我解释它,我会推荐你​​阅读任何关于并行编程的优秀教科书的共享数据部分。或维基百科:http://en.m.wikipedia.org/wiki/Race_condition