Seite 1 von 1

[Gelöst]Verständnisfrage QThread

Verfasst: 25. November 2009 20:28
von Alex66955
Hey Community,

Habe folgende Verständnisfrage:
Für eine Rechenintensive Funktion lager ich diese in ein Thread aus damit die Gui bedienbar bleibt.

Die Klasse von der aus dieser Thread aufgerufen wird soll an dieser Stelle warten bis die die Rechenoperation im Thread beendet wird.
1. Das bewirkt man ja mit exec() am Schluss in der run() Methode der abgeleiteten Thread Klasse und das aufrufen dieser mit run()?

In dem Thread sollen auch Werte an die ProzessBar übergeben werden
2. Das erledige ich mit der Übergabe eines Zeigers der ProzessBar mittels Parameter der run() Methode?

Ich bin mir nicht sicher wie ich die Dinge richtig angehe. Mein jetziger Ansatz startet den Thread und wartet auf das ende dessen. Jedoch bleibt die Gui nicht bedienbar

Thread cpp

Code: Alles auswählen

void ProzessThread::run(ProcessBar &dialogProcessBar){

ANWEISUNGEN
exec();
}

Main cpp

Code: Alles auswählen

   dialogProcessBar.show();

   ProzessThread *thread1 = new ProzessThread;
    thread1->run(dialogProcessBar);

Verfasst: 25. November 2009 21:24
von Christian81
Einen Thread startet man nicht mit run() (ist ja protected...) sondern per start().
Datenübergabe würde ich entweder per Signal-Slot machen oder als Member der abgeleiteten QThred-Klasse.
Vom Thread auf die GUI zugreifen geht nicht.

Steht aber alles in den Beispielen - siehe z.B. das Mandelbrot-Example...

Verfasst: 27. November 2009 10:19
von Alex66955
Christian81 hat geschrieben: Vom Thread auf die GUI zugreifen geht nicht.
Aber ich kann doch per Signal/Slot Werte an die Gui übergeben wärend der Thread läuft?

Habe mir ein Kleines Programm zusammengebastelt um die Sache mit den Threads besser zu verstehen. Im Thread befindet sich eine Endlosschleife und sendet per SIGNAL nach jedem Durchlauf den Zähler eins hoch. In der Main Methode verbinde ich das Signal mit einer Progressbar die sich in einem Dialog befindet. Die Gui ist nicht bedienbar. Was mache ich Falsch?


thread.cpp

Code: Alles auswählen

void Thread::run(){

    emit setRange(10000000);

    for(long i=0; i < 100000000; i++){

        emit setAkt(i);

        if(i==10000000){
            emit setAkt(0);
            i=0;
        }
    }
        exec();
}
main.cpp

Code: Alles auswählen

dia = new Dialog(this);
neuThread = new Thread;

 connect(neuThread, SIGNAL(setAkt(long)), dia, SLOT(setAkt(long)));
 connect(neuThread, SIGNAL(setRange(long)), dia, SLOT(setRang(long)));
 connect(neuThread, SIGNAL(started()), dia, SLOT(show())); 
 connect(neuThread, SIGNAL(finished()), dia, SLOT(deleteLater()));

 connect(action, SIGNAL(triggered()), neuThread, SLOT(start()));


Verfasst: 27. November 2009 12:02
von RHBaum
warum rufst du in der run methode des threads noch mal das exec auf ?
damit geht dein Thread selber ein ne endlos schleife, und kann nur noch durch Nachrichten / Signal/slot aufrufe beendet werden !
Lass ihn einfach auslaufen, wenn er das getan hat wozu er bestimmt war ...

und versuch es mal mit paar konservativerern einstellungen ^^
du laesst den thread "vollasst" laufen, und der macht nix anderes als die signal/slot verbindung zu callen, was eine kopie der parameter bedeutet (der int wert, iss aber ned kritisch) ... die er dann als parameter einer nachricht in der eventloop des gui threads hinterlegt.
Das heisst, der erzeugt was er kann nachrichten (interne fuer signal slot connections) an deiner mainloop. Und du wunderst dich warum die ned dazukommt, deine mouse events abzufangen ??? ^^
Grats, du hasst quasi grad ne DOS attacke auf deinen Mainthread gestartet ^^

nimm die zahl in der schleife bissi runter, und mach ne pause dazwischen (sleep) ... dann kommt auch bissi ruhe in deinen mainthread.

Übrigens, multithreading multiprozessing nuetzt gar nix, wenn man ned in der lage ist, ereignisse zu "entkoppeln" .... ^^

Wenn du unbedingt nen hochlaufen einer zaehlvariable von 0-4Millarden in einer gui in einzelschritten sehen willst, wird das updaten deiner GUI die meiste zeit beanspruchen und du kannst das zaehlen entkoppeln und in threads auslagern wie du willst, du wirst 100% Last haben, und deine gui wird sich ned ruehren ^^ bis du in paar min bei den 4millarden angelangt bist:
GUI operationen, aka malen auf dem Bildschirm dauert nun mal, auch im zeitalter von Teraflop Grakas ^^

besser: aller X millisekunden den momentanen wert der variablen abfragen und anzeigen, und auf die zwischenschritte verzichten ... und voiala, das ganze wird viel viel schneller gehen ^^

Ciao ....

Verfasst: 28. November 2009 09:27
von Alex66955
Vielen Dank für die Ausführliche Antwort. Wusste nicht das ich bei so einem einfachen Beispiel so viele Fehler reinhauen kann.
Hab mir nochmal die Doku durchgelesen und finde es interessant wie einfach zu verstehen sie ist wenn man das Grundkonzept verstanden hat.

Eine Frage hätte ich noch. Wie stellt man es am besten an mittels Abbruch Button den Thread zu beenden. Man sollte ja nicht einfach terminate benutzen da dann Datenbrei entstehen könnte. Und bei nicht gebrauch des Threads anschliesend mit deletelater löschen?


Gruß Alex

Verfasst: 28. November 2009 15:05
von franzf
Alex66955 hat geschrieben:Eine Frage hätte ich noch. Wie stellt man es am besten an mittels Abbruch Button den Thread zu beenden. Man sollte ja nicht einfach terminate benutzen da dann Datenbrei entstehen könnte. Und bei nicht gebrauch des Threads anschliesend mit deletelater löschen?
Einen SLOT in deiner Thread-Klasse, der einen bool setzt. Z.B.

Code: Alles auswählen

void MyThread::stopCalculation()
{
    m_stop = true;
}
Und in run() dann sowas:

Code: Alles auswählen

for(long i=0; i < 100000000 && !m_stop; i++){  // <- Abbruchbedingung erweitert

    emit setAkt(i); 
    if(i==10000000){ 
        emit setAkt(0); 
        i=0; 
    } 
}
Nun noch das QPushButton::clicked()-SIGNAL auf deinen stopCalculation()-SLOT connecten, und es sollte kein "Datenbrei" entstehen (wenn du auch bei m_stop==true alles richtig aufräumst).

Verfasst: 29. November 2009 10:01
von Alex66955
Dankeschön. Die Lösung gefällt mir.
Alles Beantwortet :)


Gruß Alex

Verfasst: 10. Dezember 2009 17:03
von odenter
Ich baue mir immer "DatenObjekte" die, irgendwelche Art von Daten halten, und meine GUI(s) hören auf Changed-Events der DatenObjekte.

Der Vorteil ist es ist völlig egal ob die Daten per Programm-Code geändert werden oder durch die GUI. Du hast immer nur eine Verbindung von der GUI zu den Daten. Ist recht sauber.

Verfasst: 11. Dezember 2009 10:07
von RHBaum
wie einfach zu verstehen sie ist wenn man das Grundkonzept verstanden hat.
Naja, das Grundprizip isss recht einfach, ja, nur die konsequenzen sind ...naja oft ned ueberschaubar ^^
Also ich komm auch oft noch ins straucheln .... und muss ne weile ueberlegen ....
- Aus welchen thread komm ich eigentlich ?
- welche threads verwenden diese Variable, muss ich die schuetzen oder ned ??? ...
- wie vermeide ich unnötige rechenzeit ... nen thread staendig gegen nen event / ne variable pollen lassen freut das energiemanagment ned wirklich. und sleep im thread ... naja, iss irgendwie thema verfehlt ^^ damit wird der thread träge.

Einen SLOT in deiner Thread-Klasse, der einen bool setzt. Z.B.
Kann ne beose falle sein !
nen bool iss zu 90% nen int aka 4 byte.
zu nem rest von 30% iss also ein
bool bx;
bx=true; <- keine atomare operation

Da dich unvollstaendige bools aber kaum intressieren (dich intressiert nur das 1 byte), gehts zu 99% trotzdem gut.
"schoen" isses aber trotzdem ned.

nen bool zu schuetzen mitm Mutex iss aber auch ned der heilige gral.

besser, atomare operationen verwenden (leider meist BS abhaengig) ,
aber die QT hat da was vorbereitet: QAtomicInt

oder Events(im sinne von IPC) verwenden. -> QWaitCondition

Vieles was einfach erscheint wird plötzlich wahnisnnig komplex bei multithreading.

QWaitCondition mCondTerminate;

Code: Alles auswählen

void MyThread::stopCalculation()
{
    mCondTerminate.wakeAll();
}

Code: Alles auswählen

bool bcont = true;
do
{
   //// irgendws wildes tun .... 
   
   /// Abbruch bedingungen checken 
   if(mCondTerminate.wait(NULL,0)) /// meist hat man aber nen Mutex zum entlocken fuer den 1. parameter ... fuer 0 millisekunken aber eh witzlos. 
   {
      bcont = false;
   }
} while(bcont);
Sicher ned der performanteste Weg, aber MT technisch sauberer.
wie gesagt der einfache bool weg wird unter 32bit wahrscheinlich immer gehen, unter 64 bit hoechstwahrscheinlich auch gehen, iss aber ned MTed gedacht.
Sobald deine Abbruchbedinung erweiters aka ... && !m_stop && !mPause .... wird das ganze viel undefinierter.

Muss auch zugeben ich mach mit der QT und MT ned allzuviel. Ich mach mehr auf der winapi direkt ...

Ciao ...