Multithreading 正在创建一些不确定的线程,但没有终止

Multithreading 正在创建一些不确定的线程,但没有终止,multithreading,delphi,Multithreading,Delphi,我对多线程应用程序(ip扫描器)有疑问。当我将较大的ip范围(如192.168.0.1到192.168.5.1)和线程限制设置为99时。因此,当我运行我的应用程序时,一次应该有101个线程运行(99个线程(ScannerChild)+主线程+Scannerthread),当扫描完成时,99个ScannerChild和1个Scannerthread将被终止,此时只有1个线程应该运行(主线程)。但有时线程数会变为102,扫描后线程数不会变为1,它会在任务管理器中将threadcount显示为2。有什

我对多线程应用程序(ip扫描器)有疑问。当我将较大的ip范围(如192.168.0.1到192.168.5.1)和线程限制设置为99时。因此,当我运行我的应用程序时,一次应该有101个线程运行(99个线程(ScannerChild)+主线程+Scannerthread),当扫描完成时,99个ScannerChild和1个Scannerthread将被终止,此时只有1个线程应该运行(主线程)。但有时线程数会变为102,扫描后线程数不会变为1,它会在任务管理器中将threadcount显示为2。有什么问题吗

扫描线代码 /

扫描码

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;
构造函数ScannerChild.Create(CreateSuspended:Boolean;IP:String);
开始
继承的创建(CreateSuspended);
//FCriticalsection:=TCriticalSection.create//创建临界截面
IPToScan:=IP;
结束;
//扫描儿童的执行程序
程序ScannerChild.Execute;
变量
主输出:TListItem//用于添加listitems的Listitem变量
主机名:字符串//主机名声明为字符串
开始
尝试
main输出:=LvHostname.Items.Add//将项目添加到mainoutput
mainpoutput.Caption:=IPToScan;
主机名:=IPAddressName(IPToScan);
如果主机名为EmptyStr,则
开始
mainpoutput.SubItems.Add(IPAddrToName(IPToScan))//显示输出
终点
其他的
mainpoutput.subitems.add('No host');
最后
结束;
结束;
//scannerch线程终止时调用此事件
过程Scannerthread.ScanchildTerminated(发送方:ToObject);
开始
ScannerChCount:=ScannerChCount-1;//递减扫描数
结束;

这里有很多问题。我将给你一些一般性的建议,同时尝试回答你的问题

正如我们前面所说的,在主线程之外访问GUI是错误的。不必再重复了,请回顾您之前的问题

线程的设计很差。如果你想问一个高层次的问题,我们可以帮你解决。如果您提出一个问题,让我演示一个简单的线程池,我会很高兴

除了线程设计的问题外,您还没有分离关注点。没有模块化。线程、任务和GUI代码都是相互混合的。您需要将关注点分开,以使代码可维护并具有良好的功能。如果你只问我们如何设计你的程序,而不是修复你薄弱的设计中的错误,我们可以帮助你

所有对睡眠的调用和轮询都是这种糟糕设计的症状。不应该睡觉

您的代码有太多的注释,使其难以阅读。没有必要对像
i:=i+1
这样的语句进行注释。其影响不言而喻

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

至于您所问的问题,在
ScannerChCount
变量上存在数据竞争。因此,线程可能正确终止,但计数不正确

使用
联锁增量
联锁增量
以线程安全的方式修改它。这在子终止代码和控制器线程中都存在

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


如果您还不知道什么是数据竞争,那么您太快就开始了多线程编程。与我的解释不同,我想让你参考任何关于并行编程的优秀教科书中的共享数据部分。或维基百科:

做一些调试。添加一些诊断代码,以确定无法关闭的线程被卡住的位置。您确实需要学习如何调试。这并不一定意味着在调试器中。添加跟踪调试。并且您仍在远离主线程访问GUI控件。这件事已经告诉你很多次了。为什么要问你是否忽略了回复?这让我们很恼火。你认为我们会一次又一次地告诉你同样的事情吗?我们从帮助人们学习中得到回报。如果你不能倾听,那么我们就得不到回报。让它值得我们花时间。您还创建和销毁了许多线程。创建100个线程,让他们完成任务,直到完成为止。谢谢你,我一定会遵循这一点
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;