Top Banner
Qt Concurrency Ynon Perek
56

Qt multi threads

Jul 08, 2015

Download

Technology

Ynon Perek

How to write multi threaded applications using Qt:

In the slides you'll learn about 3 alternatives, all of which allow running tasks simultaneously in Qt applications, and understand the use cases leading to choosing each.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Qt multi threads

Qt ConcurrencyYnon Perek

Page 2: Qt multi threads

Agenda

• Doing things simultaneously

• Using the event loop

• Using threads

• Using QtConcurrent algorithms

Page 3: Qt multi threads

The Event Loop

MainLoop

HandlerHandlerHandler

event 1

Page 4: Qt multi threads

You’re Already Doing It

• Network code runs in parallel

• Timers

Page 5: Qt multi threads

Unfortunately …

• DB queries block

• Disk operations block

• CPU operations block

Page 6: Qt multi threads

Latency Is The Enemy

Page 7: Qt multi threads

What We Need

• Run something “in the background”

• A single task

• A worker thread

• A full algorithm

• Another application

Page 8: Qt multi threads

Single TaskQThread QThreadPool QRunnable Thread Synchronization

Page 9: Qt multi threads

Threads Theory

• Define tasks by extending QRunnable

• Use QThreadsPool to run them

Page 10: Qt multi threads

Demo Runnable

class MyTask : public QRunnable { virtual void run() { for ( int i=0; i < 10; i++ ) { qDebug() << "[" << QThread::currentThreadId() << "]" << " " << i; } } };

Page 11: Qt multi threads

Using The Thread Pool

QCoreApplication a(argc, argv); MyTask *t1 = new MyTask;

QThreadPool p; p.start(t1);

p.waitForDone();

Page 12: Qt multi threads

Thread Pool Notes

• start() takes ownership of the runnable. It will be deleted when done

• Number of threads matches number of CPU cores

• waitForDone() stops the event loop. Be careful with that one

Page 13: Qt multi threads

Yielding

• If you feel your thread has worked hard enough, rest with:QThread::yieldCurrentThread();

Page 14: Qt multi threads

Sharing

• Cool in real life

• Uncool for threads

Page 15: Qt multi threads

Sharing Problems

• Shared resources can be manipulated from other threads

• Even when you’re in the middle

Page 16: Qt multi threads

Sharing Problems

class Counter { public: Counter() { n = 0; }

void increment() { ++n; } void decrement() { --n; } int value() const { return n; }

private: int n; };

Can you use the code below from multiple threads ?

Why ?

Page 17: Qt multi threads

Sharing Problems

• Most Qt and C++ code is re-entrant

• It means you can’t access same instance from different threads at the same time

Page 18: Qt multi threads

Quiz

• Assume m_text is a QStringList

• Can you use the code from multiple threads ? Why ?

virtual void run() { m_text.append(m_a); for ( int i=0; i < 100; i++ ) { if ( m_text.last() == m_a ) { m_text.append(m_b); } else { m_text.append(m_a); } } }

Page 19: Qt multi threads

Thread Safety

• Code is marked thread-safe if it’s ok to use it from multiple threads, on the same instance.

• Thread-safe code manages data access

Page 20: Qt multi threads

Other Considerations

• When locking threads, you lose concurrency

• Previous example was better written by:

• Separating the problem to sections

• Solving each section in a thread

Page 21: Qt multi threads

Locking Options

• QMutex

• QSemaphore

• QReadWriteLock

• QWaitCondition

Page 22: Qt multi threads

QMutex

• Only one thread can “hold” a mutex

• Others wait till done

• Like the java’s synchronized keyword

Page 23: Qt multi threads

QMutex Demo

• Consider the two methods on the right

• If called from multiple threads, they’ll break

int number = 6;

void method1() { number *= 5; number /= 4; }

void method2() { number *= 3; number /= 2; }

Page 24: Qt multi threads

QMutex Demo

• But the mutex changes everything

• Now method2 has to wait for method1 to finish

QMutex mutex; int number = 6;

void method1() { mutex.lock(); number *= 5; number /= 4; mutex.unlock(); }

void method2() { mutex.lock(); number *= 3; number /= 2; mutex.unlock(); }

Page 25: Qt multi threads

Deadlocks

• Forgetting to unlock a mutex creates deadlocks

• Unlocking in a wrong order creates deadlocks

Page 26: Qt multi threads

QMutexLocker

With QMutexLocker, you’ll never forget to unlock your mutex

virtual void run() { QMutexLocker l(&mutex); num = 6; m1(); m2(); qDebug() << QThread::currentThreadId() << ") n = " << num; }

Page 27: Qt multi threads

Q & A

Page 28: Qt multi threads

Lab

• Modify Counter code so it is thread safe

Page 29: Qt multi threads

QReadWriteLock

• Multiple reads, single write

• Prevents starvation

Page 30: Qt multi threads

Demo• Write a QRunnable class to run the

following code

• Did you segfault ? Good, now fix it

virtual void run() { if ( m_writer ) { m_list << QString::number(qrand()); } else { qDebug() << m_list; } }

Page 31: Qt multi threads

QReadWriteLock vs. QMutex

• Use QReadWriteLock when you have many readers and few writers

• For other cases, mutex is sufficient

Page 32: Qt multi threads

QSemaphore

• Producer-Consumer problem

• One producer, multiple consumers

Page 33: Qt multi threads

QSemaphore

• A general counting semaphore

• Methods:

• acquire(n)

• release(n)

Page 34: Qt multi threads

Demo Code

QSemaphore sem(5); // sem.available() == 5

sem.acquire(3); // sem.available() == 2 sem.acquire(2); // sem.available() == 0 sem.release(5); // sem.available() == 5 sem.release(5); // sem.available() == 10

sem.tryAcquire(1); // sem.available() == 9, returns true sem.tryAcquire(250); // sem.available() == 9, returns false

Page 35: Qt multi threads

Locking Alternatives

• Locking mechanisms are used to sync with worker threads

• Some alternatives:

• Using QtConcurrent algorithms

Page 36: Qt multi threads

Q & A

Page 37: Qt multi threads

Qt Style Worker Thread

Main Event Loop

Secondary Event Loop

Signals and Slots

Page 38: Qt multi threads

The Code

• Create a worker thread as a normal QObject

• Move it to another thread

• Start the thread’s event loop

MyWorker w; QThread t;

w.moveToThread(&t); t.start();

Page 39: Qt multi threads

Why Is It Awesome

• Write code with normal signals and slots

• Make it multi-threaded when needed

• Almost no change

Page 40: Qt multi threads

Under The Hood

• QObject::connect uses a message queue to call slots in other threads

Page 41: Qt multi threads

Lab

• Write a GUI app that displays an image

• Use QFileDialog to choose image file

• Read image file from a worker thread

Page 42: Qt multi threads

Concurrent Algorithms

Page 43: Qt multi threads

Concurrent Algorithms

• Algorithms on collections can make use of concurrent primitives

• Qt provides:

• map

• filter

• reduce

Page 44: Qt multi threads

Let’s Start With A Demoint main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QList<long> seq; QTime t1,t2;

for (long i=2; i < 100000; i++ ) seq << i;

t1.start(); QtConcurrent::blockingFiltered(seq, isPrime); qDebug("Time elapsed (Multi): %d ms", t1.elapsed());

t2.start(); foreach ( long n, seq ) isPrime(n); qDebug("Time elapsed (Single): %d ms", t2.elapsed());

return 0; }

Page 45: Qt multi threads

Results

Time elapsed (Multi): 1433 ms

Time elapsed (Single): 3408 ms

Page 46: Qt multi threads

The Good Parts

• Easily implement algorithms on collections

• Avoid common mistakes

• No need to synchronize threads

Page 47: Qt multi threads

Other Primitives

• map applies a function to each item in the collection, returning a list of the results

• mappedReduced does map and reduces the result

Page 48: Qt multi threads

Other Primitives

• filter applies a function on each item in the collection, returning a list of the “true” ones

• filteredReduced does the same, and also reduces to a single result

Page 49: Qt multi threads

Progress Indication

• Algorithms return QFuture object

• Use QFutureWatcher to add signals and slots

Page 50: Qt multi threads

Demo Code

QFutureWatcher<long> w; ProgressReporter p;

w.setFuture(QtConcurrent::filtered(seq, isPrime));

QObject::connect(&w, SIGNAL(progressRangeChanged(int,int)), &p, SLOT(progressRangeChanged(int,int))); QObject::connect(&w, SIGNAL(progressValueChanged(int)), &p, SLOT(progressValueChanged(int)));

Page 51: Qt multi threads

Progress Notes

• QFutureWatcher is templated to the type of the list

• Progressive filling is possible with:

• resultReadyAt(int)

• resultsReadyAt(int,int)

• Best gain: no latency

Page 52: Qt multi threads

Q & A

Page 53: Qt multi threads

Concurrency Takeaways

• Use worker threads for IO

• Use QtConcurrent for algorithms

• Latency is the enemy

Page 54: Qt multi threads

Lab

• Move our prime number detection code to a GUI app

• User selects range, and the application prints all prime numbers in range

• Try with and without QtConcurrent

Page 55: Qt multi threads

Online Resources

• http://qt-project.org/doc/qt-5.0/qtcore/thread-basics.html

• http://www.greenteapress.com/semaphores/

• http://qt-project.org/videos/watch/threaded_programming_with_qt

Page 56: Qt multi threads

Thanks For Listening

• Ynon Perek

[email protected]

• http://ynonperek.com