17.2.2.  Parallel Prime Number Calculators

[ fromfile: threads.xml id: primethreads ]

Example 17.14. src/threads/PrimeThreads/primeserver.h

[ . . . . ]
class PrimeServer : public QObject
{
    Q_OBJECT
public:
    explicit PrimeServer(QObject* parent =0);
    void doCalc(int numThreads, int highestPrime, bool concurrent = false);
    int nextNumberToCheck();
    void foundPrime(int );   
    bool isRunning() const;
public slots:
    void cancel();
private slots:
    void handleThreadFinished();
signals:
    void results(QString);
private:
    int m_numThreads;
    bool m_isRunning;    
    QList<int> m_primes;
    int m_nextNumber;
    int m_highestPrime;
    QTime m_timer;
    QMutex m_nextMutex;
    QMutex m_listMutex;
    QSet<QObject*> m_threads;
private slots:
    void handleWatcherFinished();
    void doConcurrent();
private:
    bool m_concurrent;
    int m_generateTime;             1    
    QFutureWatcher<void> m_watcher;
};
[ . . . . ]

1

Time spent generating input data.


Example 17.15. src/threads/PrimeThreads/primeserver.cpp

[ . . . . ]

void PrimeServer::
doCalc(int numThreads, int highestPrime, bool concurrent) {
    m_isRunning = true;
    m_numThreads = numThreads;
    m_concurrent = concurrent;
    m_highestPrime = highestPrime;
    m_primes.clear();
    m_primes << 2 << 3;
    m_threads.clear();
    m_nextNumber = 3;
    m_timer.start();
    if (!concurrent) {
         for (int i=0; i<m_numThreads; ++i) {
            PrimeThread *pt = new PrimeThread(this);    1
            connect (pt, SIGNAL(finished()), this,
                     SLOT(handleThreadFinished()));
            m_threads << pt;
            pt->start();                                2
         }
    }
    else doConcurrent();
}

1

Child thread is not started yet.

2

Child thread executes run().


Example 17.16. src/threads/PrimeThreads/primethread.h

#ifndef PRIMETHREAD_H
#define PRIMETHREAD_H

#include <QThread>
#include "primeserver.h"

class PrimeThread : public QThread
{
    Q_OBJECT
public:
    explicit PrimeThread(PrimeServer *parent);
    void run();                         1
private:
    PrimeServer *m_server;

};

#endif // PRIMETHREAD_H

1

Required override.


Example 17.17. src/threads/PrimeThreads/primethread.cpp

[ . . . . ]
PrimeThread::PrimeThread(PrimeServer *parent) 
: QThread(parent), m_server(parent) { }

void PrimeThread::run() {
    int numToCheck = m_server->nextNumberToCheck();
    while (numToCheck != -1) {
        if (isPrime(numToCheck)) 
            m_server->foundPrime(numToCheck);
        numToCheck = m_server->nextNumberToCheck();
    }
}
[ . . . . ]

Example 17.18. src/threads/PrimeThreads/primeserver.cpp

[ . . . . ]

int PrimeServer::nextNumberToCheck() {
    QMutexLocker locker(&m_nextMutex);              1
    if (m_nextNumber >= m_highestPrime) {
        return -1;
    }
    else {
        m_nextNumber+= 2;
        return m_nextNumber;
    }
}

void PrimeServer::foundPrime(int pn) {
    QMutexLocker locker(&m_listMutex);              2
    m_primes << pn;
}

1

Scope-based mutex works from multiple return points.

2

This method also must be made thread-safe.


Example 17.19. src/threads/PrimeThreads/primeserver.cpp

[ . . . . ]

void PrimeServer::cancel() {
    QMutexLocker locker(&m_nextMutex);
    m_nextNumber = m_highestPrime+1;

Example 17.20. src/threads/PrimeThreads/primeserver.cpp

[ . . . . ]

void PrimeServer::handleThreadFinished() {
    QObject* pt = sender();                         1
    m_threads.remove(pt); 
    pt->deleteLater();
    if (!m_threads.isEmpty()) return;               2
    int numPrimes = m_primes.length();
    QString result = QString("%1 mutex'd threads %2 primes in %3"
                             "miliseconds. ").arg(m_numThreads)
                             .arg(numPrimes).arg( m_timer.elapsed());
    QString r2 = QString(" %1 kp/s")
                  .arg(numPrimes / m_timer.elapsed());
    qDebug() << result << r2;
    emit results(result + r2);
    m_isRunning = false;
}

1

The QThread is our sender.

2

Others are still running.


Figure 17.6.  Speedup Factor of PrimeThreads

Speedup Factor of PrimeThreads

QtConcurrent approach

Example 17.21. src/threads/PrimeThreads/primeserver.h

[ . . . . ]
 
private slots:
    void handleWatcherFinished();
    void doConcurrent();
private:
    bool m_concurrent;
    int m_generateTime;             1    
    QFutureWatcher<void> m_watcher;
};

1

Time spent generating input data.


Example 17.22. src/threads/PrimeThreads/primeserver.cpp

[ . . . . ]

void PrimeServer::doConcurrent() {
    QThreadPool::globalInstance()->setMaxThreadCount(m_numThreads);
    m_primes.clear();
    m_primes << 2;
    for (m_nextNumber=3; m_nextNumber<=m_highestPrime; 
         m_nextNumber += 2) {
       m_primes << m_nextNumber;
    } 
    m_generateTime = m_timer.elapsed();
    qDebug() << m_generateTime << "Generated " 
             << m_primes.length() << " numbers";
    connect (&m_watcher, SIGNAL(finished()), this,
             SLOT(handleWatcherFinished()));
    m_watcher.setFuture(                            1
        QtConcurrent::filter(m_primes, isPrime));   2
}

void PrimeServer::handleWatcherFinished() {
    int numPrimes = m_primes.length();
    int msecs = m_timer.elapsed();
    QString result = 
      QString("%1 thread pool %2 primes in %4/%3 milliseconds"
              "(%5% in QtConcurrent).") .arg(m_numThreads)
              .arg(numPrimes).arg(msecs).arg(msecs-m_generateTime)
              .arg((100.0 * (msecs-m_generateTime)) / msecs);
    QString r2 = QString(" %1 kp/s").arg(numPrimes / msecs);
    qDebug() << result << r2;
    m_watcher.disconnect(this);
    emit results(result + r2);
    m_isRunning = false;
}

1

QFutureWatcher for monitoring progress.

2

Non-blocking, in-place filter() returns a QFuture.