2015-07-21 87 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從主線程只和數據使用沒有任何來自其他線程訪問的調用,所以沒有必要爲一個關鍵部分。

使用線程池還將有助於提高應用程序的響應能力。