我对多线程应用程序(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;
答案 0 :(得分:4)
这里有很多问题。我会给你一些一般的建议,并尝试回答你的问题。
正如我们之前所说,您在主线程之外访问GUI是错误的。无需再次讨论,请回顾之前的问题。
您的线程设计很差。如果您要问一个关于我们的高级问题,我们可以帮助您解决问题。如果您提出一个允许我演示简单线程池的问题,我会很高兴。
除了线程设计的问题之外,您还没有关注点。没有模块化。线程和任务以及GUI代码都是相互混合的。您需要将关注点分开,以使代码可维护且考虑周全。如果您只问我们如何设计您的程序而不是修复您的弱设计中的错误,我们可以帮助您。
对Sleep
的所有调用和投票都是这种糟糕设计的症状。应该没有睡觉。
你的代码有太多评论,难以阅读。没有必要评论像i := i+1
这样的陈述。这种影响是不言而喻的。
您需要学习如何调试线程代码。交互式调试器不是那么有用。它会干扰线程执行的时间。使用跟踪日志记录来调试此类问题。在你学会如何做到这一点之前,你不能指望取得进步。我再说一遍,学习如何调试至关重要。
关于您询问的问题,您在ScannerChCount
变量上进行了数据竞争。所以线程可能正确终止,但你错误地计算它们。
使用InterlockedIncrement
和InterlockedDecrement
以线程安全的方式对其进行修改。这是在子终止代码和控制器线程中。
您可能认为这不是必需的,因为递减计数器的ScanChildTerminated
是一个OnTerminate
事件,因此由主线程执行。但是,在主线程中不执行递增计数器的控制器线程代码。
如果您还不知道数据竞争是什么,那么您很快就会启动多线程编程。而不是我解释它,我会推荐你阅读任何关于并行编程的优秀教科书的共享数据部分。或维基百科:http://en.m.wikipedia.org/wiki/Race_condition。