2015-07-21 77 views
1

我有一个多线程应用程序,需要通过idhttp发送数据到一些http主机......主机的数量不同,我把它们放在一个TXT文件中,读入一个TStringList中。但每天约有5k主机。好吧,运行3天后,或多或少,以及大约15k主机检查,线程开始挂在代码的某一点,程序变得非常慢,就像它开始每10分钟检查一台主机一样......有时候会发生远远的,并保持1周运行非常好,但在这个相同的问题后:看起来像大多数线程开始挂起......我不知道问题到底在哪里,因为我用100个线程运行它,就像我说的之后,15K或多个主机也开始变得缓慢......Delphi执行多线程后挂起的线程

这里的几乎全部源代码(抱歉发帖全部,但我认为这是更好小于以上)

type 
    MyThread = class(TThread) 
    strict private 
    URL, FormPostData1, FormPostData2: String; 
    iData1, iData2: integer; 
    procedure TerminateProc(Sender: TObject); 
    procedure AddPosted; 
    procedure AddStatus; 
    function PickAData: bool; 
    function CheckHost: bool; 
    function DoPostData(const FormPostData1: string; const FormPostData2: string): bool; 
    protected 
    constructor Create(const HostLine: string); 
    procedure Execute; override; 
    end; 

var 
    Form1: TForm1; 
    HostsFile, Data1, Data2: TStringList; 
    iHost, iThreads, iPanels: integer; 
    MyCritical: TCriticalSection; 

implementation 

function MyThread.CheckHost: bool; 
var 
    http: TIdHTTP; 
    code: string; 
begin 
    Result:= false; 
    http:= TIdHTTP.Create(Nil); 
    http.IOHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(http); 
    http.Request.UserAgent:= 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'; 
    http.HandleRedirects:= True; 
    try 
    try 
     code:= http.Get(URL); 
     if(POS('T2ServersForm', code) <> 0) then 
     Result:= true; 
    except 
     Result:= false; 
    end; 
    finally 
    http.Free; 
    end; 
end; 

function MyThread.PickAData: bool; 
begin 
    Result:= false; 
    if (iData2 = Data2.Count) then 
    begin 
     inc(iData1); 
     iData2:= 0; 
    end; 
    if iData1 < Data1.Count then 
    begin 
     if iData2 < Data2.Count then 
     begin 
      FormPostData2:= Data2.Strings[iData2]; 
      inc(iData2); 
     end; 
     FormPostData1:= Data1.Strings[iData1]; 
     Result:= true; 
    end; 
end; 

function MyThread.DoPostData(const FormPostData1: string; const FormPostData2: string): bool; 
var 
    http: TIdHTTP; 
    params: TStringList; 
    response: string; 
begin 
    Result:= false; 
    http:= TIdHTTP.Create(Nil); 
    http.Request.UserAgent := 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'; 
    http.Request.ContentType := 'application/x-www-form-urlencoded'; 
    params:= TStringList.Create; 
    try 
    params.Add('LoginType=Explicit'); 
    params.Add('Medium='+FormPostData1); 
    params.Add('High='+FormPostData2); 
    try 
     response:= http.Post(Copy(URL, 1, POS('?', URL) - 1), params); 
     if http.ResponseCode = 200 then 
     Result:= true; 
    except 
     if (http.ResponseCode = 302) then 
     begin 
      if(POS('Invalid', http.Response.RawHeaders.Values['Location']) = 0) then 
      Result:= true; 
     end 
     else 
     Result:= true; 
    end; 
    finally 
    http.Free; 
    params.Free; 
    end; 
end; 

procedure MyThread.AddPosted; 
begin 
    Form1.Memo1.Lines.Add('POSTED: ' + URL + ':' + FormPostData1 + ':' + FormPostData2) 
end; 

procedure MyThread.AddStatus; 
begin 
    inc(iPanels); 
    Form1.StatusBar1.Panels[1].Text:= 'Hosts Panels: ' + IntToStr(iPanels); 
end; 

procedure MainControl; 
var 
    HostLine: string; 
begin 
    try 
    MyCritical.Acquire; 
    dec(iThreads); 
    while(iHost <= HostsFile.Count - 1) and (iThreads < 100) do 
    begin 
     HostLine:= HostsFile.Strings[iHost]; 
     inc(iThreads); 
     inc(iHost); 
     MyThread.Create(HostLine); 
    end; 
    Form1.StatusBar1.Panels[0].Text:= 'Hosts Checked: ' + IntToStr(iHost); 
    if(iHost = HostsFile.Count - 1) then 
    begin 
     Form1.Memo1.Lines.Add(#13#10'--------------------------------------------'); 
     Form1.Memo1.Lines.Add('Finished!!'); 
    end; 
    finally 
    MyCritical.Release; 
    end; 
end; 

{$R *.dfm} 

constructor MyThread.Create(const HostLine: string); 
begin 
    inherited Create(false); 
    OnTerminate:= TerminateProc; 
    URL:= 'http://' + HostLine + '/ServLan/Controller.php?action=WAIT_FOR'; 
    iData2:= 0; 
    iData1:= 0; 
end; 

procedure MyThread.Execute; 
begin 
    if(CheckHost = true) then 
    begin 
    Synchronize(AddStatus); 
    while not Terminated and PickAData do 
    begin 
     try 
     if(DoPostData(FormPostData1, FormPostData2) = true) then 
      begin 
      iData1:= Data1.Count; 
      Synchronize(AddPosted); 
      end; 
     except 
     Terminate; 
     end; 
    end; 
    Terminate; 
    end; 
end; 

procedure MyThread.TerminateProc(Sender: TObject); 
begin 
    MainControl; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    if (FileExists('data2.txt') = false) OR (FileExists('data1.txt') = false) then 
    begin 
     Button1.Enabled:= false; 
     Memo1.Lines.Add('data2.txt/data1.txt not found!!'); 
    end; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    openDialog : TOpenDialog; 
begin 
    try 
    HostsFile:= TStringList.Create; 
    openDialog := TOpenDialog.Create(Nil); 
    openDialog.InitialDir := GetCurrentDir; 
    openDialog.Options := [ofFileMustExist]; 
    openDialog.Filter := 'Text File|*.txt'; 
    if openDialog.Execute then 
    begin 
    HostsFile.LoadFromFile(openDialog.FileName); 
    Button2.Enabled:= true; 
    Button1.Enabled:= false; 
    end; 
    finally 
    openDialog.Free; 
    end; 
end; 

procedure TForm1.Button2Click(Sender: TObject); 
begin 
    Button2.Enabled:= false; 
    Data1:= TStringList.Create; 
    Data1.LoadFromFile('data1.txt'); 
    Data2:= TStringList.Create; 
    Data2.LoadFromFile('data2.txt'); 
    MyCritical:= TCriticalSection.Create; 
    iHost:= 0; 
    iThreads:= 0; 
    MainControl; 
end; 

回答

0

只是乍一看,我建议添加http:= TIdHTTP(无)到TT hread.Create事件和http.Free到TThread的Destroy事件。不知道这是否能解决问题。 Windows对每个进程的线程确实有操作系统限制(记不起来了,但是想到了63的数字,你可能想要创建一个线程池来缓存你的线程请求,它可能会更加可靠地执行一个“雷鸣群”请求,我怀疑这些请求的数量有些线程可能会异常终止,这可能会减慢速度,泄漏内存等。启用FullDebugMode和LogMemoryLeakDetailsToFile检查泄漏可能会揭示某些内容。检查任务管理器以观察内存所使用的运行的进程是一个问题的另一个微温的指示符;存储器使用的增长和永远不会释放

最好的运气

RP

1

首先,我会陷阱&日志异常。

其次,这似乎无限建立Form1.Memo1。以这种方式运行内存时发生了什么?或者超过它的容量。 (我已经处理了Delphi的时间已经很长了,我不记得这方面是否有限制,当然这是32位代码。)

+0

Humm ...我想我只是在Memo1中添加行。我不建立它,我只是使用Add.Lines方法... – user3810691

+0

@ user3810691是的,你正在添加行 - 它们可能是无限的。这些线需要空间! –

2

IMO,你的线程被困在MyThread.Execute while循环中。不能保证一旦进入该循环就会退出(因为DoPostData()方法依赖于某些外部响应)。通过这种方式,我敢打赌,每个线程都会停留在那里,直到很少(或者没有)线程继续工作。

您应该为MyThread.Execute()添加一些日志功能,以确保它不会在某个地方死去......您还可以在其中添加一个失败安全退出条件(例如,如果(TriesCount>一百万次)然后退出)。

而且,我认为更好的设计,以保持运行所有的时间你的线程,只是提供了新的工作给他们,而不是创建/销毁线程,即在开始创建100个线程,只在最后消灭他们你的程序执行。但它需要对代码进行重大更改。

+0

这是我认为正在发生的事情,他们被卡在执行的某个地方,一个接一个,直到没有任何工作......并且我认为是在CheckHost()或DoPostData()上,因为两者都使用IdHTTP。但我试图用try/finally来缓解任何问题 - 尝试/除了...我不知道在这种情况下可以做些什么来始终退出此功能。你有没有任何伪代码? 关于新设计,我将这个实现与时间做得更好,一开始我总是遇到访问冲突,今天我已经解决了这个问题。但是我对这种语言的了解还不足以完成你提出的设计。 – user3810691

+0

我想你应该从上面建议的FreeOnTerminate:= true开始,看看你得到了什么。 – Alexandre

2

您不断创建线程而不释放它们。这意味着你的系统会在一段时间后长出资源(窗口句柄或内存)。

在线程构造函数中设置FreeOnTerminate := true在终止时释放线程。

如果您在调试模式下启动程序时声明了ReportMemoryLeaksOnShutdown := true,则会报告此泄漏。


MainControl从主线程只和数据使用没有任何来自其他线程访问的调用,所以没有必要为一个关键部分。

使用线程池还将有助于提高应用程序的响应能力。