Zdeněk Krejčí

Zdeněk Krejčí

Stránky na téma programování v C++
Návod na QT Multithreading
Tento návod jsem se rozhodl napsat protože pro mě i po studování různé QT Multithreading dokumentace byla vícevláknová aplikace stále noční můrou.Po pochopení systému jak s nimi pracovat, bych toto rád sdílel s jinými, kteří jsou ted tam kde jsem byl já v ... :-) Základy tu popisovat by bylo asi zbytečné, jelikož ty nejsou tak složité a stačí se podívat na internet, kde jich je opravdu hodně. Na druhou stranu těch co popisují některé úskalí jsem sám nenašel.Takže bych pojal tento návod jako upřesnění neboli osvětlení, některých problematik více vláknových aplikací. To znamená že pokud nemáte žádnou skušenost s vlákny tak by bylo dobré si nejdříve přečíst nějaký tutoriál, se základy Multithreadingu.

Vytvoření vlákna:

Takže řekněme že napíšeme jednoduchý program, který bude inkrementovat hodnotu nějaké proměnné a zobrazovat ji v panelu LCD.

Vytvoříme hlavní třídu, tedy formulář MainWindow na kterém toto budeme zobrazovat. Tento bude obsahovat Button, Button2,Button3 a LCD display nebo cokoli jiného třeba Label.Pak by jste samozřejmě museli převádět hodnotu inkrementující se proměnné na integer, proto bude jednodušší vytvořir LCD.
Proměnná kterou budeme incrementovat, si nazveme třeba pass.

Button1=spuštění vlákna
Button2= zastavení vlákna
Button3=Zobrazit stav proměnné pass
LCD=zobrazování stavu proměnné pass

Po té, si k němu napíšeme další třídu thread což bude pracovní vlákno, ve které se bude incrementovat proměnná.
Toto vlákno bude obsahovat funkci:
off() = vypnutí vlákna

takže celé to bude vypadat asi takhle:

//Vytvoření globální instance thread

thread th;



/************ Třída s vláknem ***********/

class thread : public QThread{

Q_OBJECT

public:

thread();

~thread();

void off();

void run();

private:

bool stopped;

};

/******************* GUI třída *****************/


class MainWindow : public QMainWindow

{

Q_OBJECT

public:

MainWindow(QWidget *parent = 0);

~MainWindow();

private:

Ui::MainWindow *ui;

private slots:

void on_pushButton_3_clicked();

void on_pushButton_2_clicked();

void on_pushButton_clicked();

};
/******************* Implementace *****************/

void MainWindow::on_pushButton_clicked()

{

th.start();

}

void MainWindow::on_pushButton_2_clicked()

{

th.stop();

    pass = 0;

}

void MainWindow::on_pushButton_3_clicked()

{

ui->lcdNumber->display(pass);

}

thread::thread()

{

stopped = false;

}

thread::~thread(){


}

void thread::run(){

while(!stopped){

pass ++;

if(pass == 100)pass = 1;

if(stopped)return;

}

}

void thread::stop(){

stopped = true;

}


Z kódu je patrné, že v hlavní GUI třídě MainWindow je deklarovaná privátní třída thread, což je naše pracovní třída. Deklarovaná je tam proto, aby jsme měli k této třídě thread, přístup z třídy MainWindow.
Takže po spuštění programu, tedy MainWindow se nám ukáže formulář, kde se klikem na Button1 zavolá funkce th.start(), která spustí pracovní thread tím, že QT zavolá run(), kde máme loop while !stopped to znamená, že dokud nezavoláme od jinud, to je z naší MainWindow th.stop() tak se thread nezastaví. Takže jakmile se tak stane thread se opravdu zastaví. No a my máme vyhráno.  !OMYL!
Zastavili jsme vlákno ale skusme ho znovu zapnout, logicky by to mělo jít ale nepujde.
Protože reference th na třídu thread pořád existuje s naší proměnou stopeed = false. To znamená že stopped = true která je v konstruktoru vlákna, se už znovu neuplatní. Takže by jsme mohli klikat na th.start() do zblbnutí a nic by se nedělo. A tato malá věc, se nikde záhadně nepíše a nikdo se o ni nezminuje.. Byt takovýchto příkladů na na internetu spousta.
Takže jediné co je třeba zajistit, aby se nastavovala stopped na true pokaždé co chceme vlákno znovu rozjet.
Takže vlákno musí obsahovat ještě jednu fuknkci a to on() pozor ne start() tu má už je rezervovaná v QThread pro samotné spouštění vlákna.
Takže asi takto:

void thread::on(){

stopped = false;

}


a tu pak budeme volat th.on() před navoláním funkce th.start() v metodě Button1.
Pozor opravdu dost návodů říká že se vlákno stopne voláním th.exit(),th.quit(),terminate a podobně.Ano to je sice pravda ale ne v tomhle případě. Příklad exit() by mělo smysl jen pokud by jsme měli ve funkci run(), namýsto while loopu exec() který začne event loop.
Takže jsou to všechno docela zajímavé vychytávky... Více o funkcích exit quit a terminate najdete zde.
A ted je to opravdu celé... GOOD LUCK
Name
Email
Comment
Or visit this link or this one