c/c++: suchfunktion erzeugt 100% prozessor-auslastung

tes

Vice Admiral Special
Mitglied seit
08.09.2002
Beiträge
603
Renomée
9
Standort
bei Augsburg
hi,

also, ich schreibe seit laengeren an einem reg-suchtool.
und die primaere suchfunktion erzeugt eine 100% auslastung des prozessors.

wenn im hauptfenster die suche ausgeloest wird, erhalte (ich als user) die kontrolle erst wieder, wenn die suche beendet wurde.

welche techniken gibt es um eine funktion ressourcen-schonender auszufuehren und bei bedarf abzubrechen?
hilft mir hier evtl. thread-programmierung weiter?
 
Yup, die einzige Lösung ist in dem Fall, die Suche in einen eigenen Thread auszulagern.
 
danke fuer die antworten.
beim stichwort multithreading hab ich mich mal eingelesen, und hab jetzt ne kleine testanwendung fertig.
es ist eine winapi-gui, die auf button-click einen thread startet und per button-click beendet.
werd ich demnaechst mal implementieren.

soll ich meinen ersten versuch posten, fuer die allgemeinheit?
 
Je nachdem welche Software du nimmst kannst du auch einen "Sleep" einbauen. Damit bekommst du zwar nicht die Kontrolle über die Software, jedoch wird die CPU-Last gesenkt. Vorsicht: je nachdem wo du den Sleep einbaust, explodiert deine Laufzeit. Am besten machst du nur 1ms Pause: Sleep(1).

Die Alternative mit dem Thread ist so glücklich auch nicht: dann zieht der separate Thread eben 100% Systemlast.

WEnn du unter Windows programmierst hast du zwei schöne Möglichkeiten:

1.) Du senkst die Priorität des Programms. Das ist nicht soo einfach, aber die beste Möglichkeit. Im Prinzip hast du damit erreicht, was ein separater Thread dir auch ermöglicht hätte. Dort kannst du die Prio beim Erstellen auch angeben. Um die Prio der Hauptanwendung zu senken, brauchst du als erstes einen Handle auf dessen Thread. Mittels diesem kannst du dann über eine Systemfunktion die Prio senken.

2.) Bei Borland gibt es den Aufruf "Application->ProcessMessages();". Damit führt das Programm User-getriggerte Befehle aus. Ob es das bei MS auch gibt (nehm ich mal an) und wie das heisst, weiss ich leider nicht.

Aber mit den letzten beiden Möglichkeiten müsstest du auch hinkommen.
 
Original geschrieben von Exr
2.) Bei Borland gibt es den Aufruf "Application->ProcessMessages();". Damit führt das Programm User-getriggerte Befehle aus. Ob es das bei MS auch gibt (nehm ich mal an) und wie das heisst, weiss ich leider nicht.
Bei VB heißt es DoEvents. Bei C/C++ gibt's für sowas glaube ich ein Verfahren namens Message-Pumping.
 
Application->ProcessMesages() ist aus Borland C++Builder.
Bei Borland kanns aber schon sein dass die das von der Pascal-Seite her benannt haben...

gn8,
exr
 
Original geschrieben von Exr
Am besten machst du nur 1ms Pause: Sleep(1).

Dummerweise kann windows das nicht so genau. Die Maximale Genauigkeit liegt bei 10 ms .... Sleep(1) und Sleep(10) haben also genau des gleichen Effekt: 10 ms "Schlaf"
 
Wow. Danke. Wusste ich nicht. Je nach Anwendung wird das echt zum Killer.... *grübel*

Eigentlich ist der "Sleep" auch recht doof. Aber immer noch besser als 100% CPU-Last wenns nicht wirklich dringend ist.

exr
 
Original geschrieben von Exr
Die Alternative mit dem Thread ist so glücklich auch nicht: dann zieht der separate Thread eben 100% Systemlast.

Wenn ich seine Frage richtig verstanden habe, geht’s ihm wohl weniger darum, die Systemauslastung (die ja im Sinne einer möglichst schnellen Suche durchaus wünschenswerterweise bei 100% liegen sollte) zu senken. Er will die Reaktivität der GUI und des restlichen Systems gewährleisten. Da sollten zwei separate Threads (Such-Thread mit niedrigerer Priorität, GUI-Thread mit normaler Priorität) effizienter sein, als n*10 ms Zwangspause. Bei einer single-threaded Lösung, bei der nur der Haupt-Thread mit niedrigerer Priorität läuft, reagiert zwar Windows besser. Das ändert aber nichts daran, daß die programmeigene GUI weiterhin nicht oder nur träge reagiert.
Also:
Quick and Dirty: Sleep(n);
Sauber und effizient: hThread = CreateThread(...);
 
Es geht aber auch mit der kooperativen Methode, wie es z.B. unter Win16 üblich war und bei vielen kooperativen Betriebssystemen im embedded Bereich teilweise immer noch ist.

Unter Win32 mit C in etwa so im main() Teil der Anwendung:

PHP:
RunCount = 0;
while (ProgramRunning)
{
   if (!PeekMessage( &msg,0,0,0,PM_REMOVE))
   {
      // Keine Nachricht in der Queue, ein paar Searches durchführen.
      if (++RunCount < RUN_COUNT_MAX)
      {
         SearchOnePart();
         continue;
      }
   }
   RunCount = 0;

   // Standard Windows-Nachrichtenschleife:
   while (GetMessage(&msg, NULL, 0, 0))
   {       
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
}
Die Suchfunktion muß halt aufgebrochen sein, d.h. mit jedem Aufruf ein Stück weitersuchen.
Wieviel SearchOnePart() genau macht, muß ermittelt werden, genauso der Wert von RUN_COUNT_MAX.
Dann lässt sich das Ganze so tunen, dass System und Anwendung bedienbar bleiben.

Ciao,
Ray
 
Das ist meines Wissens das, was man als Message Pumping bezeichnet.
 
Original geschrieben von TiKu
Das ist meines Wissens das, was man als Message Pumping bezeichnet.
Stimmt. ;)
Solchen Code hab ich Jahre bevor mir der Begriff "Message Pumping" übern Weg lief geschrieben. Bin vorhin über ein Stück altes Programm gestolpert, weshalb ich's auch gepostet habe.
An für sich dürfte man mit der Threading-Methode besser fahren und muß dabei vor allem seinen Algorithmus nicht künstlich aufbrechen. Gut ist aber, beide Verfahren zu kennen, für den Fall, dass Threading nicht zur Verfügung steht.

@TiKu: Man erkennt Dich ja kaum wieder... *buck*
 
oh, hier tut sich ja noch was. :)

fuer PseudoReal:

ich hab den about-dlg einer hallo welt win32-anw missbraucht (vc++6).
3x buttons (IDC_START, IDC_END, IDOK)
1x edit (IDC_STATUS)
1x static (IDC_DESCR)
1x combobox (IDC_REFRESH) typ: dropdown

Code:
// thread_test.cpp : Definiert den Einsprungpunkt für die Anwendung.
//

#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
#include <windows.h>	//CreateThread()

#define RSTRING "thread running "

HINSTANCE hDlgInstance;
BOOL repeat = TRUE;		//globaler stopflag
BOOL running = FALSE;	//globaler runningflag

LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);

void initComponents(HWND);
DWORD CBIndex2Refresh(HWND);

void startThread(HWND);
unsigned long __stdcall checkRepeat(void*);
void endThread();

//////////////////////////////////////////////////////////////////////////////////////////
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
 	hDlgInstance = hInstance;
	DialogBox(hDlgInstance, (LPCTSTR)IDD_ABOUTBOX, NULL, (DLGPROC)About);
	
	return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case WM_INITDIALOG:
			initComponents(hDlg);
			return TRUE;

		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				case IDOK:
				case IDCANCEL:
					if(running)
					{
						//stopflag setzen
						endThread();
						//warte bis wirklich die threadzeit abgelaufen ist
						Sleep( (CBIndex2Refresh(hDlg)+1) );
					}
					EndDialog(hDlg, LOWORD(wParam));
					return TRUE;
				break;

				case IDC_START:
				{
					//thread erzeugen und starten
					startThread(hDlg);
				}
				break;

				case IDC_END:
				{
					//thread beenden
					endThread();
				}
				break;
			}
	}
    return FALSE;
}

//////////////////////////////////////////////////////////////////////////////////////////
void initComponents(HWND hParent)
{
	//fenster-titel
	SetWindowText(hParent, "ThreadTest");

	//textfelder
	SetDlgItemText(hParent, IDC_STATUS, "current thread status");
	SetDlgItemText(hParent, IDC_DESCR, "refresh time:");

	//buttons
	SetDlgItemText(hParent, IDC_START, "start thread");
	SetDlgItemText(hParent, IDC_END, "end thread");
	SetDlgItemText(hParent, IDOK, "close");

	//combobox
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"0,1 sec");
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"0,3 sec");
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"0,5 sec");
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"1,0 sec");
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"1,5 sec");
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_ADDSTRING, 0, (LPARAM)"2,0 sec");
	//default: nach erzeugen, den 3ten markieren
	SendDlgItemMessage(hParent, IDC_REFRESH, CB_SETCURSEL, WPARAM(2), 0);

}

//////////////////////////////////////////////////////////////////////////////////////////
DWORD CBIndex2Refresh(HWND hParent)
{
	int cbIndex;

	cbIndex = SendDlgItemMessage(hParent, IDC_REFRESH, CB_GETCURSEL, 0, 0);
	switch(cbIndex)
	{
		case 0:
			return(100L);//millisekunden
		case 1:
			return(300L);
		default:
		case 2:
			return(500L);//default auswahl
		case 3:
			return(1000L);
		case 4:
			return(1500L);
		case 5:
			return(2000L);
	}
	return(NULL);
}

//////////////////////////////////////////////////////////////////////////////////////////
void startThread(HWND hParent)
{
	HANDLE hMyThread; 
    DWORD id;

	if(!running)
	{
		repeat = TRUE;
		hMyThread = CreateThread(NULL, 0, checkRepeat, hParent, 0, &id);
	}
}

//////////////////////////////////////////////////////////////////////////////////////////
unsigned long __stdcall checkRepeat(void *hParent)
{
	char out[18];
	char *points[6]={"",".","..","..."," ..","  ."};
	int pos = 0;

	running = TRUE;

	while(repeat)
	{
		

		Sleep(CBIndex2Refresh((HWND)hParent));
		sprintf(out, "%s%s", RSTRING, points[pos]);
		if(pos == 5)
			pos=0;
		else
			pos++;
		SetDlgItemText((HWND)hParent, IDC_STATUS, out);
	}	

	running = FALSE;
	SetDlgItemText((HWND)hParent, IDC_STATUS, "thread stopped");

	return(0);//dummy-ret
}

//////////////////////////////////////////////////////////////////////////////////////////
void endThread()
{
	repeat = FALSE;
}

wie einige vorgeschlagen haben, werd ich das ganze mit zwei threads verwirklichen.
einen control-thread, der einen worker-thread startet/beendet.
wie einige versuche zeigten, bringt ein sleep im worker-thread eine deutlich geringere prozessorlast und andere evtl. laufende anwendungen bekommen auch noch ein bisserl rechenzeit ab.

ich hatte auch schon an ein eingrenzen der suche auf eine bestimmte anzahl von ergebnissen gedacht, und bei "nichts tun" um die anzahl weiter fortgesetzt wird.
aber dafuer muesst ich die suche neu schreiben, was mir jetzt zu aufwaendig erscheint.
zudem muesst ich mir einige variablen merken, damit ich die suche fortgesetzen kann.
ich glaub mit threads fahr ich besser.

ich danke fuer die rege anteilnahme. :)
 
Zuletzt bearbeitet:
Hi,

ich habe ein Thread erzeugt, mit dem Glauben, dass ich so die 100% CPU Auslastung vermeiden kann. Ich dachte, dass eine Endlosschleife in einem Thread die CPU nicht sehr auslastet, was sie ansonsten eben tut. Nun, ich habe mich geirrt. Im Grunde ist das irgendwie logisch aber ich habe es mit eingenen Augen gesehen, dass es nicht so sein muss. Es handelt sich um ein borland C++ Programm, was so ähnlich funktioniert wie mein Progeamm aber viel mehr Funktionen hat und mehr tut, aber eben nur mit ca. 5% die CPU auslastet.

Ich Programmiere mir VC 6.0 und vorerst nur in WinAPI.

Gibt es da ein Weg? In dem Programm gibt es auch ein sleep(1) aber bei meinem Programm macht das kein Unterschied.

In einem Tutorial habe ich in dem Kappitel über Threads gelesen, dass eine Thread Funktion niemals explizit aufgeruffen werden darf, was ist damit gemeint?

MfG,

Athomi
 
hi athomi,

nach meinen erfahrungen, zieht ein thread auch volle cpu-auslastung.
aber man hat die moeglichkeit, einen thread mit geringerer prioritaet zu starten(siehe function SetThreadPriority() http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/setthreadpriority.asp ).
soll die rechenaufwaendige berechnung bis zum ende durchgefuehrt werden, reicht eigentlich ein thread mit geringer prio oder halt der sleep.

EDIT: hier hab ich mich verzettelt, man kann natuerlich auch einen einzelnen thread beenden.
das beweist ja die testanwendung.

ich probiere derzeit eine zweite moeglichkeit aus.
die rechenaufwaendige berechnung lass ich in einer nicht sichtbaren konsolen-anwendung laufen.
das hat auch den vorteil, das ich bei veraenderungen der berechnungsroutine nur die konsolen-anwendung austauschen brauch und die gui kann bleiben wie sie ist.
zudem biete ich dem user die moeglichkeit, die suche auch per commandline ohne gui zu starten.
auch hier hat man die moeglichkeit, die prio der konsolenanwendung festzulegen und vorzeitig zu beenden.
im prinzip ist es das selbe wie mit einem thread.
ob es geschwindigkeitsmaessig etwas bringt, weiss ich noch nicht.
aber die oben genannten vorteile sprechen fuer sich

die frage des expliziten aufrufs kann ich so nicht beantworten.
es ist nicht genau ersichtlich in welchen zusammenhang das gemeint ist.
vorstellen koennt ich mir, das eine funktion, die bereits in einem thread laeuft, nicht zur laufzeit des threads ein weiteres mal aufgerufen werden darf (prozedural gesehen).
dazu koennen sich sicher die anderen aeussern.

gruss tes
 
Zuletzt bearbeitet:
Hi,

vielen Dank für die ausführliche Antwort.

Die Frage der Priorität meines Threads wird mein Problem nicht lösen und eine Konsolenanwendung läuft auch fast immer mit 100% CPU Auslastung, zumindest bei mir da auf jeden Fall eine Dauerschleife läuft.
Mein thread rettet mich in so fern, da ich meine GUI problemlos benutzen kann und ich habe festgestellt, dass wenn ich meine Routine mit SetTimer alles 30 ms starte, ich so die CPU Auslastung auch ca 30% reduzieren kann.
Wenn ich eine Lösung habe werde ich sie im diesem Forum vorstellen.

MfG,

Athomi
 
hi athomi,

naja, bei mir ist es ja wuenschenswert, das die suche mit vollem power laeuft.
sie soll ja so schnell wie moeglich fertig sein.
ein heruntersetzen der prio bringt bei mir den vorteil, das der user auch gleichzeitig noch andere programme bedienen kann.
die meisten programme werden ja mit NORMAL_PRIORITY_CLASS, bzw THREAD_PRIORITY_NORMAL fuer threads, gestartet.
da meines eine niedrigere prio hat, bekommt es die restliche cpu-power ab, solang eine andere anwendung rechenzeit fuer sich beansprucht.

einen sleep hatte ich auch ausprobiert, aber da war mir die suchdauer dann doch ein bisserl arg lang.
ich parse ja jeden key, namen und wert der registry auf vorkommen eines suchstrings.
da die registry mit der zeit auf mehrere mb im zweistelligen bereich anschwellen kann,
siehst du mit welchem datenaufwand ich beschaeftigt bin bzw die cpu, hehe.
da kam wirklich kein sleep in frage.

der entwickler muss halt entscheiden, was fuer ihn wichtig ist.

und wofuer willst du eine loesung posten? eine endlosschleife zieht immer max. rechenkraft, ausser du verzoegerst sie mit einem timer.

gruss tes
 
Zuletzt bearbeitet:
Zurück
Oben Unten