TIE-11300 Tietotekniikan vaihtuva-alainen kurssi Graafisen käyttöliittymän ohjelmointi Syksy 2013 Luento 10 Rinnakkaisuus käyttöliittymäohjelmoinnissa Juha-Matti Vanhatupa
Rinnakkaisuus ja käyttöliittymäohjelmointi Käyttöliittymäohjelmoinnissa rinnakkaisuudella estetään käyttöliittymän jumiutuminen -> reagoi aina käyttäjän toimenpiteisiin Käyttöliittymän tulee antaa palautetta käyttäjän pitkäkestoisten toimenpiteiden sujumisesta (esim. progress bar, odotusanimaatiot) Käyttäjän tulee myös voida peruuttaa tekemänsä toimenpiteet
Prosessi vs Säie Prosessi Muistin suojauksen yksikkö Säie Suorituksen yksikkö
GUI-Thread Message queue Main Thread creates Worker thread Worker thread Worker thread
GUI-Thread Message queue Variable use Main Thread creates use Worker thread Worker thread Worker thread
GUI-Thread Message queue Main Thread creates message Worker thread Worker thread Worker thread
Rinnakkaisuus QT:ssa Tarjoaa käyttöjärjestelmäriippumattoman tavan rinnakkaiseen ohjelmointiin QThread QMutex QRunnable QThreadPool QAtomicInt GUI thread (main thread) QCoreApplication::exec()
Säikeen luominen class MyThread : public QThread { Q_OBJECT protected: void run(); }; void MyThread::run() { }
Säikeen käynnistäminen MyThead* pthread = new MyThread(); pthread->start(qthread::normalpriority); http://qt-project.org/doc/qt- 5.1/qtcore/qthread.html#Priority-enum
Säikeen käynnistäminen QApplication (tai QCoreApplication jos kyseessä komentorivisovellus) olio luotava ennen säikeiden luontia.
Säikeen elinkaari ja signaalit Luonti start() -> suoritus alkaa (started() -signaali) Suoritus kutsuu run() funktiota Suoritus loppuu kun run() :sta palataan. (finishedsignaali) Suoritus voidaan myös lopettaa väkisin kutsumalla terminate() (terminated()-signaali)
Säikeen sanomajono Säikeelle voi luoda oman sanomajonon exec(); // QThread Tarpeellinen, jos halutaan käyttää timereita Lähettää signaaleja toisesta threadista Lähettää eventtejä Tuhota objekteja deletelater-kutsulla Huom! Widgettien käsittely mahdollista vain GUIthreadista!
Säikeen sanomajono Event jono OK Säie1 obj1 Emit signal Säie2 obj2 Emit signal Virhe!
Säikeiden välinen kommunikointi Jaetun muuttujan avulla Signaalien avulla
QObject Objektit elävät threadissa QObject::thread(); Objektin omistavaa säiettä voi vaihtaa QObject::moveToThread(); Myös kaikki lapsi-objektit siirtyvät Siirrettävällä objektilla ei saa olla parenttia Objektiin asennetun event filterin tulee myös olla samassa säikeessä kuin tarkailtava objekti.
QObject Luominen Objektin luontivaiheessa myös mahdollinen parent-objekti pitää olla samassa säikeessä Tuhoaminen Objektin tuhoaminen toisesta threadistä ei ole sallittua QObject:: deletelater(); skeduloi objektin tuhottavaksi. Objekti tuhotaan kun suoritus palaa event-loop:iin.
QObject Jäsenfunktioiden kutsuminen QObject ei ole automaattisesti säieturvallinen, vaan pitää käyttää mutexeja Poikkeuksena signaalien kytkeminen
Reentrant Luokka on reentrant jos sen jokaista jäsenfunktiota voidaan kutsua yhtäaikaa eri säikeistä eri olioille. (ei jaettua dataa) class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; };
Thread safe Luokka on säieturvallinen, jos sen olion jäsenfunktioita voidaan kutsua yhtä aikaa eri säikeistä. class Counter { public: Counter() { n = 0; } void increment() {QMutexLocker locker(&mutex); ++n; } void decrement() {QMutexLocker locker(&mutex); --n; } int value() const {QMutexLocker locker(&mutex); return n; } private: mutable QMutex mutex; int n; };
Reentrant ja thread safe Reentrant thread safe a() b() c() Oliot a() b() c() a() b() c() Olio a() b() c() a() b() c() a() b() c() Säikeet Säikeet
QMutex Metodeja void lock() bool trylock() void unlock() Lukko pitää avata samasta säikeestä missä se lukittiin.
QMutexLocker Lukitsee mutexin koko funktiokutsun ajaksi
Säikeen sanomajono Objekti voi lähettää signaalin vaikka säikeellä ei ole sanomajonoa Säikeessä elävä objekti ei voi vastaanottaa signaaleja tai eventtejä toisesta säikeestä, jos säikeellä ei ole sanomajonoa Signaalien kytkeminen on säieturvallista
Signaalien kytkeminen Tapahtuu oletuksena eri tavalla riippuen siitä ovatko lähettäjä ja vastaanottaja samassa vai eri säikeessä Sama säie: signaalin emitointi vastaa funktiokutsua Eri säie: signaali menee säikeen sanomajonon kautta
Signaalien kytkeminen Kytkennän tyyppi määritellään antamalla tyyppi connect funktion parametrina. connect(theard1, SIGNAL(finished()), this, SLOT(OnThreadhQuit()),Qt::QueuedConnection);
Signaalien kytkeminen enum Qt::ConnectionType Qt::DirectConnection Toimitetaan välittömästi slot:iin. Slot suoritetaan signaalin lähettäneessä säikeessä. Qt::QueuedConnection Signaali on jonossa kunnes säikeen sanomajono toimittaa sen slot:lle. Slot suoritetaan vastaanottavassa säikeessä. Qt::BlockingQueuedConnection Sama kuin QueuedConnection, mutta säikeen suoritus pysäytetään kunnes signaali on toimitettu. Tulee käyttää vain kun vastaanottaja on toisessa säikeessä. Väärinkäyttö voi johtaa ohjelman jumiutumiseen.
Signaalien kytkeminen Qt::AutoConnection (default) Sama kuin DirectConnection, jos lähettäjä ja vastaanottaja ovat samassa säikeessä. Sama kuin QueuedConnection, jos lähettäjä ja vastaanottaja ovat eri säikeissä. Qt::UniqueConnection Sama kuin AutoConnection, mutta tarkistaa että signaalin ja slot:n välille ei ole jo luotu yhteyttä. Qt 4.6 alkaen.
QThreadPool Hallinnoi säie-joukkoa. Vähentää säikeiden luomiskustannuksia kierrättämällä säie-olioita. Jokaisella Qt-sovelluksella globaali QThreadPool-instanssi, voidaan pyytää kutsumalla QThreadPool::globalInstance() Oletuksena tuhoaa hallinnoimansa QRunnable-oliot.
Qt: Käyttöliittymän jumiutumisen estäminen Käytetään useaa säiettä Pitkäkestoisen operaation aikana kutsutaan tiuhaan processevents() funktiota. Jotta muut eventit tulee käsiteltyä Käytetään 0 ms:n timeria, joka laukeaa aina kun muita eventtejä ei ole. Aina kun timer laukeaa suoritetaan varsinaista tehtävää.
QtConcurrent Tarjoaa korkeamman tason rajapinnan rinnakkaisuuden toteuttamiseen Rajapintaa käyttävät ohjelmat skaalautuvat automaattisesti tulevaisuuden mahdollisesti isommille prosessorimäärille Julkaistu Qt 4.4 versiossa Osa QtCore moduulia
QtConcurrent QImage scaled(const QImage& image) { return image.scaled(qsize(100,100)); } Const QList<QImage>images = const Qlist<Qimage> thumbnails = QtConcurrent::mapped(images, scaled); Esimerkkikoodi ajaa scaled operaation jokaiselle kuvalle käyttäen yhtä montaa säiettä kuin laitteessa on prosessoreja.