Speicherleck bei Thread
-
Hallo,
ich habe eben bemerkt, dass in meiner Anwendung offenbar ein Speicherleck
vorhanden ist. Im taskmanager sehe ich bei der Speicherauslastung für meinen
Prozess, dass sobald ich einen Thread aktiviere urplötzlich der Speicherverbrauch
im gleichbleibenden Abstand jeweils um 4k steigt.
Irgendwann schmiet mir die anwendung dann einfach mit einem Laufzeitfehler ab.Ich bin mir sicher, dass es an dem Thread selbst liegt, nicht an der
Funktion, die er ausführen soll. Denn rufe ich die Funktion manuell x-mal
auf, steigt der Speicherverbrauch nur einmalig um 4K an.Vielleicht köönt ihr einen Blick darauf werfen, was ich da grob falsch gemacht
haben könnte (was vermutlich nicht wenig sein wird
):void running::OnBnClickedCheck1() { ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck(); if(1 == ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck()) { GetDlgItem(IDC_EDIT1)->EnableWindow(true); if(m_Flag==0) { m_Flag = 1; //kontrolliert den thread CWinThread* pThread = AfxBeginThread (thrFunction, this); } } else { GetDlgItem(IDC_EDIT1)->EnableWindow(false); if(m_Flag==1) { m_Flag = 0; } } }Ist meine Vermutung, dass "CWinThread* pThread = AfxBeginThread (thrFunction, this);" nur einmalig gestartet werden darf (OnInitDialog) richtig?
-
Nein!
Du kannst mehrere Threads starten.Fragwürdig alleine ist aber schon die Weitergabe eines CWnd* an einen anderen Thread!
CWnd Objekte sind threadafin. Der Zugriff ist nur sehr eingeschränkt möglich.
-
Hallo Martin,
ich möchte den Thread nur einmalig starten also Status "An" oder "Aus",
damit sich in meinem Listcontrol die Werte aus einem Webserver aktualisieren.
Das ListControl selbst liegt in einem Tabcontrol.Diese Aktualisierung selbst treibt den Speicher nicht in die Höhe, da
manuell aufgerufen das Problem mit dem Speicher nicht auftritt. Nur in
Verbindung mit dem Thread.Daher habe ich jetzt
CWinThread* pThread = AfxBeginThread (thrFunction, this);in OnInitDialog verbannt.
Das Problem besteht aber leider weiter. Daher mal etwas mehr von meinem Code:
BOOL finished::OnInitDialog() { CDialog::OnInitDialog(); //... ((CButton*)GetDlgItem(IDC_CHECK1))->SetCheck(true); m_Flag=1; CWinThread* pThread = AfxBeginThread (thrFunction, this); return TRUE; } UINT finished::thrFunction(LPVOID pParam) { finished* pDlg = (finished*) pParam; pDlg->more(); return 0; } void finished::more(void) { CString wt; int waittime; while(m_Flag) { GetDlgItem(IDC_EDIT1)->GetWindowText(wt); if(wt=="") { wt="3"; } waittime=atoi(wt); waittime=waittime*1000; Sleep(waittime); updatelist(); // daten vom server abrufen..klappt manuell ohne speicherleck bei mehrmaligem rufen! } void finished::OnBnClickedCheck1() { ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck(); if(1 == ((CButton*)GetDlgItem(IDC_CHECK1))->GetCheck()) { GetDlgItem(IDC_EDIT1)->EnableWindow(true); if(m_Flag==0) { m_Flag = 1; } } else { GetDlgItem(IDC_EDIT1)->EnableWindow(false); if(m_Flag==1) { m_Flag = 0; } } } }Wenn ich in der Checkbox das Häckchen entferne, also den Thread stoppe,
bleibt der hohe Speicherverbrauch zumindet schon mal stehen...
Nicht gut...
-
Ich bekomme das Leck nicht raus..werde wohl erstmal zwangsweise auf nen Timer ausweichen.

-
Schon mal probiert m_bAutoDelete auf false zu setzen und dich dannn selbst um die Entsorgung deines CWinThread-Objektes zu kümmern?
Hatte mal ein ähnliches Problem und das war, warum auch immer, die Lösung.
-
Hi AndyDD,
ich bin in der Threadsache nicht sehr bewandert muss ich zugeben...Du meinst ich soll pThread.m_bAutodelete; dort einfügen, wo ich auch m_Flag=0;
setze? Mehr nicht?
-
Ich habe das Speicherleck ja insbesondere wenn der Thread läuft...beenden kann ich ihn, dann wird aber kein Speicher freigegeben.
In der Laufphase powert sich der Speicher dann immer weiter hoch. Habe schon gedacht der Thread wird versehentlich durch einen Fehler
von mir immer wieder ausgeführt und nicht gekillt. Aber auch das scheint nicht der Fall zu sein, da ich testweise eine MessageBox in die aufzurufende Funktion gepackt habe und die dann nach Beendigung des Thread auch tatsählich nicht mehr kommt...
-
Also m_bAutoDelete ist auf TRUE per defualt.
An dem was ich an Code hier sehe kann es nicht liegen.
Ich tippe mal auf einen Fehler in updatelist!
BTW: Ich sehe nicht, dass die Funktion more komplett hier angegeben wurde.
-
Ist es korrekt dass Du das Leck auch hast, wenn Du void finished::more(void) leer ist?
Simon
-
Vielleicht ist es das ListControl selbst das mit Daten gefüllt wird..
Versuch doch mal Abschnitte aus dem Updatelist oder das Updatelist selbst auszukommentieren um die Problemzeilen einzugrenzen..
-
@theta: wenn ich more leer lasse habe ich kein Leck = Speicher unverändert.
@Martin: in more() ist nicht mehr drin als das hier gepostete.
Ich kann gerne updatelist() posten..ist ein wildes gefrickel (schonmal sorry dafür!) :Egal, man will ja was lernen...los gehts:
void finished::updatelist(void) { m_list1.DeleteAllItems(); CString url,saveto; saveto=lnk.pfadzurexe(); saveto+="DATA.ini"; char *pBuf1 = new char[300]; CString returnstring1; GetPrivateProfileString("DATA","User","",pBuf1,300,saveto); returnstring1.Format("%s",pBuf1); delete []pBuf1; char *pBuf12 = new char[300]; CString returnstring12; GetPrivateProfileString("DATA","Password","",pBuf12,300,saveto); returnstring12.Format("%s",pBuf12); delete []pBuf12; char *pBuf13 = new char[300]; CString returnstring13; GetPrivateProfileString("DATA","IP","",pBuf13,300,saveto); returnstring13.Format("%s",pBuf13); delete []pBuf13; char *pBuf14 = new char[300]; CString returnstring14; GetPrivateProfileString("DATA","Port","",pBuf14,300,saveto); returnstring14.Format("%s",pBuf14); delete []pBuf14; if(returnstring13!="") { int prt = atoi(returnstring14); char *pBuf14a = new char[300]; CString returnstring14a; GetPrivateProfileString("DATA","Savein","testpath",pBuf14a,300,saveto); returnstring14a.Format("%s",pBuf14a); delete []pBuf14a; returnstring14a.Replace("\\","%2F"); CInternetSession session; CHttpConnection* pConnection = session.GetHttpConnection(_T(returnstring13),(INTERNET_PORT)prt,returnstring1,returnstring12); CHttpFile* pFile = pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, _T("/cgi-bin/xyz/Gcaller.cgi?set=x")); BOOL result = pFile->SendRequest(); // bool result =true; url="http://"; url+=returnstring13; url+=":"; url+=returnstring14; url+="/"; url+="cgi-bin/xyz/Gcaller.cgi?set=x"; int cnt = 0; int curPosxx= 0; int xxcnta =0; CString strHtmlDoc; int curPos = 0; int counter =0; int f =0; if(result) { char character = NULL; CStdioFile* pStdioFile = session.OpenURL(url, 1, INTERNET_FLAG_TRANSFER_BINARY); int found = -1; while (pStdioFile->Read(&character, 1)) { strHtmlDoc += character; } pStdioFile->Close(); CString zeigen,wert,resToken,vals,del,m_sBody; wert =strHtmlDoc; vals=strHtmlDoc; CString str = strHtmlDoc; int curPos= 0; int nr =0; resToken= str.Tokenize("\n",curPos); while (resToken != "") { m_sBody=resToken; nr++; CString nrcs; nrcs.Format("%i",nr); int position1 = m_sBody.Find(','); CString w1=(m_sBody.Left(position1)); m_list1.InsertItem(0,nrcs,0); m_list1.SetItemText(0,1,w1); int position2=m_sBody.Find(',',position1+1); CString w2=m_sBody.Mid(position1+1,position2-position1-1); m_list1.SetItemText(0,2,w2); int position3=m_sBody.Find(',',position2+1); CString w3=m_sBody.Mid(position2+1,position3-position2-1); m_list1.SetItemText(0,3,w3); int position4=m_sBody.Find(',',position3+1); CString w4=m_sBody.Mid(position3+1,position4-position3-1); m_list1.SetItemText(0,4,w4); int position5=m_sBody.Find(',',position4+1); CString w5=m_sBody.Mid(position4+1,position5-position4-1); m_list1.SetItemText(0,5,w5); int position6=m_sBody.Find(',',position5+1); CString w6=m_sBody.Mid(position5+1,position6-position5-1); m_list1.SetItemText(0,6,w6); int position7=m_sBody.Find("\\",position6+1); CString w7=m_sBody.Right(m_sBody.GetLength()-1-position6); m_list1.SetItemText(0,7,w7); resToken= str.Tokenize("\n",curPos); }; delete pFile; delete pConnection; session.Close(); }else { MessageBox("fehler beim senden!"); } } }
-
-
pConnection und pFile werden nur gelöscht, wenn die Anfrage erfolgreich gesendet wurde. Ansonsten nicht.
-
Reicht bei pStdioFile ein Close oder muss es auch gelöscht werden?
-
-
Zusätzlich kannst Du die char Buffer auch auf dem Stack allozieren, dann werden sei automatisch aufgeräumt.
-
-
Ja! Es fehlt ein
delete pStdioFile;Siehe Sample:
http://msdn.microsoft.com/de-de/library/cc485620(VS.71).aspx
-
ForenFan schrieb:
@sri: du meinst delete pStdioFile ?
@theta: wie meinst du das?

So:
char pBuf1[300] = {}; CString returnstring1; GetPrivateProfileString("DATA","User","",pBuf1,300,saveto); returnstring1.Format("%s",pBuf1);
-
theta schrieb:
char pBuf1[300] = {}; CString returnstring1; GetPrivateProfileString("DATA","User","",pBuf1,300,saveto); returnstring1.Format("%s",pBuf1);Das ist ja auch Code der übeleren Sorte, besser und weitaus einfacher:
CString returnstring1; GetPrivateProfileString("DATA","User","",CStrBuf(returnstring1,300),300,saveto);Weiterhin grundsätzlich, warum machst Du so etwas:
char pBuf1[] = "something"; CString returnstring1; returnstring1.Format("%s",pBuf1);Warum nicht einfach:
char pBuf1[] = "something"; CString returnstring1(pBuf1);oder
char pBuf1[] = "something"; CString returnstring1; returnstring1 = pBuf1;
-
Ich wollte mit dem Codeschnipsel nur zeigen, dass die Allokation auf dem Stack gemacht werden kann und nicht auf dem Heap. Aber danke für die schönere, komplette Variante.
-
Hi, ich krame das Thema nochmals hoch, da ich zwischenzeitlich dachte es
hätte sich erledigt. Dem ist jedoch nicht so...Sobald ich das zweite mal in die Updatelist() komme schmiert mir die
Anwendung mit einem Runtime Error ab.Im Debugger lande ich dann in der "memcpy_s.c" dort stehe ich am Ende der
Datei bei:memcpy(dst, src, count); //HIER return 0; }Zuvor werden offenbar laut Debugger in Updatelist() diese Zeilen aufgerufen:
pConnection = session.GetHttpConnection(_T(returnstring13),(INTERNET_PORT)prt,returnstring1,returnstring12); //hier ist wohl das problem pFile = pConnection->OpenRequest(CHttpConnection::HTTP_VERB_POST, _T("/cgi-bin/xyz/List.cgi?GCaller2=x"));In inet.cpp wird bei:
void AFXAPI AfxThrowInternetException(DWORD_PTR dwContext, DWORD dwError /* = 0 */) { if (dwError == 0) dwError = ::GetLastError(); CInternetException* pException = new CInternetException(dwError); pException->m_dwContext = dwContext; TRACE(traceInternet, 0, "Warning: throwing CInternetException for error %d\n", dwError); THROW(pException); }Der Fehlercode 28674224 für dwError ausgespuckt.
Komisch ist, dass ich doch in dem Threadcall auch alles sauber wieder lösche und
er eigentlich im nächsten durchlauf mit NULL initialisieren müsste und der Fehler so in der Form nicht auftreten dürfte...
Das hier wird ja alles nach einem Durchlauf in Updatelist() abgearbeitet:
delete pStdioFile; delete pFile; delete pConnection; session.Close();Die Deklaration der Variablen CInternetSession etc. habe ich nun alledrings aus der Funktion raus in die Klasse verlegt. Das scheint wohl auch mit Schuld daran zu sein.
Komisch nur, dass es auch wenn die Deklaration in der selben Funktion liegt, irgendwann nach ca. 10 Minuten mit Runtime Error knallt.