Alternative zu suspendThread() und resumeThread()
-
Hallo Zusammen
Ich habe folgendes Problem: Ich erhalte alle 20 ms ein Datenpaket das verarbeitet werden muss und eventuell dargestellt werden soll. Die Verarbeitung muss jedes mal geschehen, die Darstellung nur wenn noch Zeit übrig bleibt bevor das neue Paket kommt. Ich habe mir zuerst sowas vorgestellt:
Pseudocode:
Thread_display (stellt das Datenpaket dar)
while (1) {
data = getDATA;
data = verarbeite(data);
t = verbleibende Zeit bis zum neuen Datenpaket
resumeThread(Thread_display)
sleep(t)
suspendThread(Thread_display)
}wobei der display Thread beim starten ein Datenpaket nimmt und anschliessend immer mit diesem weiterarbeitet, und erst wenn er dieses erfolgreich dargestellt hat, beginnt er von neuem mit dem zu dieser Zeit aktuellsten Datenpaket.
So werden zwar alle Pakete verarbeitet aber nur soviel dargestellt, wie eben Zeit übrig bleibt (eigentlich genau das was ich will). Das Problem dabei ist, dass suspendThread() SOFORT unterbricht, also auch in einer Critical Section, was natürlich zu einem anschliessenden dead lock führt.
Hat jemand eine Idee, wie man das Problem anders lösen könnte (z.B mit events)?
lg
finalcu
-
Dein Problem ist relativ einfach zu lösen.
Poste das ganze nochmal in anständiger Form, schön mit korrekt geschriebenen Funktionen, [cpp]/[code] Tags etc., dann sag' ich dir vielleicht auch wie.
-
hier nochmals in schöner Darstellung wie gewünscht:
Pseudocode:
//globale variablen old_data; new_data; //thread funktion display_function() { while (1) { //solange das Programm laufen soll, der Einfachheit halber = undendlich lange //greift zu beginn auf old_data zu und stellt dies dar ... //ganz am Ende old_data = new_data; //damit beim nächsten mal die neuen Daten dargestellt werden } } int main { //thread erstellen thread_handle = createThread(display_function); while (1) { tstart = currentTime(); new_data = getData(); //dies ist ein Blocking Call der solange wartet, bis neue Daten anliegen, daher ist es wichtig, dass das Timing stimmt und somit die while Schleife nach exakt 20ms wieder hier ist! new_data = verarbeite(new_data); t = 20ms - (currentTime() - tstart); //verbleibende Zeit bis zum neuen Datenpaket resumeThread(thread_handle); Sleep(t); suspendThread(thread_handle); } return 0; }
-
OK. Die Lösung ist wie gesagt IMO ganz einfach.
Lass den "wichtigen" der beiden Threads (in deinem Beispiel der Main-Thread) einfach mit höherer Priorität laufen (-> SetThreadPriority), und verzichte ganz auf SuspendThread/ResumeThread.Die meisten Betriebssysteme verwenden Scheduler, die Threads mit niedrigerer Priorität garkeine bis so-gut-wie keine Rechenzeit geben, solange Threads mit höherer Priorität etwas zu tun haben.
Soll heissen: wenn es wirklich nur einen Core gibt, dann kann der Display-Thread nicht laufen, wenn der Main-Thread noch Arbeit hat. Dabei ist natürlich wichtig, dass getData() keinen "busy-wait" macht, sondern "schläft" bis neue Daten verfügbar sind. Falls das nicht so sein sollte, kannst du immernoch Sleep() verwenden -- obwohl ich das nur als Notlösung in Betracht ziehen würde.
Dass der Display-Thread den Main-Thread nicht über die Critical-Section ausbremst, kann man auch relativ einfach sicherstellen. Nämlich indem man dafür sorgt, dass der Display-Thread diese immer nur ganz kurz lockt. Falls du nicht weisst wie man das hinbekommt, frag einfach nochmal.
-
Danke für deinen Lösungsvorschlag. Ich habe das mal versucht zu implementieren:
#include "windows.h" #include <iostream> using namespace std; //////////////////////////////////////// // global variables //////////////////////////////////////// struct ThreadData { HANDLE startEvent; ThreadData() : startEvent(0) { } }; //////////////////////////////////////// // functions //////////////////////////////////////// /* * Thread function that handles the display part */ DWORD WINAPI displayTHREAD(LPVOID param) { ThreadData* threadData = (ThreadData*)param; /* wait until start signal is set */ WaitForSingleObject(threadData->startEvent, INFINITE); cout << "Display thread started...\n"; while (1) { cout << "DISPLAY\n"; } return 0; } int main(int argc, char **argv) { //////////////////////////////////////// // initialization /////////////////////////////////////// /* * main thread settings */ if (SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST) == 0) { cout << "Error: " << GetLastError() << ", could not set the main thread priority\n"; } else { cout << "Main thread priority set to HIGHEST.\n"; } /* * create and run display thread */ ThreadData* threadData = new ThreadData; threadData->startEvent = CreateEvent(0,FALSE,FALSE,"StartEvent"); DWORD threadId = 0; HANDLE threadHandle = CreateThread(0,0,displayTHREAD,(void*)threadData,0,&threadId); cout << "Display thread created...\n"; if (SetThreadPriority(threadHandle,THREAD_PRIORITY_LOWEST) == 0) { cout << "Error: " << GetLastError() << ", could not set the display thread priority\n"; } else { cout << "Display thread priority set to LOWEST.\n"; } //////////////////////////////////////// // start the main loop /////////////////////////////////////// SetEvent(threadData->startEvent); while (1) { cout << "MAIN\n"; } //////////////////////////////////////// // termination and clean up /////////////////////////////////////// DWORD wait = WaitForSingleObject(threadHandle, INFINITE); delete threadData; return 0; }
Ich habe noch keine Funktionen meiner Datenklasse verwendet, lediglich einen Thread der "DISPLAY" ausgibt und den main thread, der "MAIN" ausgibt, erstellt um zu schauen ob das mit den Prioritäten klappt.
Eigentlich solte jetzt auf der Konsole nur "MAIN" ausgegeben werden, da der DISPLAY thread eine kleinere Priorität hat und deshalb nicht zum Zuge kommen sollte. Die Ausgabe sieht aber in etwa so aus:
... MAIN DISPLAY MAIN DISPLAY MAIN DISPLAY MAIN DISPLAY ...
irgendwelche Ideen wieso das so ist?
Gruss
finalcu
-
Dein main-thread wartet auf I/O (die Konsole) und blockiert solange, somit bekommt das display-thread die CPU.
-
Genau, solche "minimal" Beispiele sind eben nicht "minimal", sondern schon "zu klein". Bzw. wegen der Ausgabe auf die Konsole nicht wirklich sinnvoll.
Auch werden auf einer Dual-Core CPU meistens beide Threads gleichzeitig laufen.
Und ich würde nur die Priorität des Main-Threads hochsetzen - den Display Thread kannst du ruhig mit normaler Priorität weiterlaufen lassen.
-
Ok, das macht Sinn. So langsam nähere ich mich meinem Ziel. Ich habe nun noch ein stopEvent gemacht, damit man den display Thread auch stoppen kann, nur leider funktioniert das nicht richtig (beziehungsweise starten geht, stoppen nicht):
#include "windows.h" #include <iostream> using namespace std; //////////////////////////////////////// // global variables //////////////////////////////////////// struct ThreadData { HANDLE startEvent; HANDLE stopEvent; }; //////////////////////////////////////// // functions //////////////////////////////////////// /* * Thread function that handles the display part */ DWORD WINAPI displayTHREAD(LPVOID param) { ThreadData* threadData = (ThreadData*)param; /* wait until start signal is set */ WaitForSingleObject(threadData->startEvent, INFINITE); cout << "Display thread started...\n"; while (1) { WaitForSingleObject(threadData->stopEvent, 0); cout << "DISPLAY\n"; } return 0; } int main(int argc, char **argv) { //////////////////////////////////////// // initialization /////////////////////////////////////// SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); /* * main thread settings */ if (SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST) == 0) { cout << "Error: " << GetLastError() << ", could not set the main thread priority\n"; } else { cout << "Main thread priority set to HIGHEST.\n"; } /* * create and run display thread */ ThreadData* threadData = new ThreadData; threadData->startEvent = CreateEvent(0,FALSE,FALSE,"StartEvent"); threadData->stopEvent = CreateEvent(0,FALSE,FALSE,"StopEvent"); DWORD threadId = 0; HANDLE threadHandle = CreateThread(0,0,displayTHREAD,(void*)threadData,0,&threadId); cout << "Display thread created...\n"; //////////////////////////////////////// // start the main loop /////////////////////////////////////// SetEvent(threadData->startEvent); //... SetEvent(threadData->stopEvent); //////////////////////////////////////// // termination and clean up /////////////////////////////////////// DWORD wait = WaitForSingleObject(threadHandle, INFINITE); delete threadData; cvReleaseCapture(&capture); cvDestroyWindow("Webcam"); return 0; }
Ich möchte die Gelegenheit gleich nutzen um euch noch eine Frage bezüglich dual core Prozessoren zu stellen: Die endgültige Applikation soll möglichst unterbrechungsfrei auf einem dual core PC ablaufen. Sonst läuft nichts auf dem PC (bis auf die standard prozesse von windows), das heisst die Applikation kann alle Ressourcen verwenden. Soll ich nun einfach die Priorität des Prozesse erhöhen (siehe Code) oder den Prozess auf eine CPU auslagern und das ganze System auf die andere CPU? Was ist besser? Können zwei Tasks desselben Prozesses wirklich auf verschiedenen CPUs ablaufen?
Vielen Dank für eure Hilfe!
gruss
finalcu
-
nur leider funktioniert das nicht richtig (beziehungsweise starten geht, stoppen nicht):
Du musst halt den Returnwert von WaitForSingleObject abfragen...
while (1) { if (WaitForSingleObject(event, 0) == WAIT_OBJECT_0) break; }
Die endgültige Applikation soll möglichst unterbrechungsfrei auf einem dual core PC ablaufen. Sonst läuft nichts auf dem PC (bis auf die standard prozesse von windows), das heisst die Applikation kann alle Ressourcen verwenden. Soll ich nun einfach die Priorität des Prozesse erhöhen (siehe Code)
Ich persönlich würde es machen, aber is ne Streitfrage. Das wichtigste bei sowas ist aber immer gute Hardware (mit "braven" Treibern) zu verwenden.
oder den Prozess auf eine CPU auslagern
Nö.
und das ganze System auf die andere CPU?
Wie willst du das machen? Ich wüsste nicht dass das geht.
Was ist besser?
S.o.
Können zwei Tasks desselben Prozesses wirklich auf verschiedenen CPUs ablaufen?
Aber natürlich. Sonst würden Programme wie SQL Server ganz schön alt aussehen.