Set Thread Priority In QtConcurrent

From Qt Wiki
Revision as of 02:35, 18 May 2017 by Henrik Jensen (talk | contribs) (hmm ? Had a closing code tag but only format properly with extra newline before closing code tag)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

QThread class have method setPriority(), but QtConcurrent doesn't. So i write own implementation with using call_once function. But current C++ standard does not have this function, it's only in C+0x and BOOST. After some googling i found few implementations and select one. QAtomicInt not ideal class for create thread-safe code and i had to use few undocumented features like QBasicAtomicInt, because it POD type and can be initialized statically inside executable file before all potential parallel initializations inside concurrent threads.

#ifndef CALL_ONCE_H
#define CALL_ONCE_H

#include <QtGlobal>
#include <QAtomicInt>
#include <QMutex>
#include <QWaitCondition>
#include <QThreadStorage>
#include <QThread>

namespace CallOnce {
 enum ECallOnce {
 CO_Request,
 CO_InProgress,
 CO_Finished
 };

 Q_GLOBAL_STATIC(QThreadStorage<QAtomicInt*>, once_flag)
}

template <class Function>
 inline static void qCallOnce(Function func, QBasicAtomicInt& flag)
{
 using namespace CallOnce;
 int protectFlag = flag.fetchAndStoreAcquire((int)flag);
 if (protectFlag  CO_Finished)
        return;
    if (protectFlag  CO_Request && flag.testAndSetRelaxed(protectFlag,
 CO_InProgress)) {
 func();
 flag.fetchAndStoreRelease(CO_Finished);
 }
 else {
 do {
 QThread::yieldCurrentThread();
 }
 while (!flag.testAndSetAcquire(CO_Finished, CO_Finished));
 }
}

template <class Function>
 inline static void qCallOncePerThread(Function func)
{
 using namespace CallOnce;
 if (!once_flag()->hasLocalData()) {
 once_flag()->setLocalData(new QAtomicInt(CallOnce::CO_Request));
 qCallOnce(func, *once_flag()->localData());
 }
}

#endif // CALL_ONCE_H

This is example how to use this functions for set thread priority once per thread at run:

#include <QtGlobal>
#include <QtDebug>

#include <QTimer>

#include <QTime>

#include <QVector>
#include <QThread>
#include <QtConcurrentMap>
#include <QtConcurrentFilter>
#include <QCoreApplication>

#include <algorithm>

#include "call_once.h"

enum {Max = 100};

struct run_once
{
 void operator()()
 {
 qDebug() << "Functor: This only once…";
 }
};

void func_run_once()
{
 qDebug() << "Function: This only once…";
}

struct inc_functor
{
 inc_functor() : counter(0) {}
 inline int operator()() {return counter;}
 int counter;
};

struct setPriorityFunctor
{
 setPriorityFunctor(QThread::Priority priority = QThread::NormalPriority)
 : m_priority(priority) {}
 inline void operator()()
 {
 QThread* thread = QThread::currentThread();
 thread->setPriority(m_priority);
 }
 QThread::Priority m_priority;
};

void setLowestPriorityFunction()
{
 QThread* thread = QThread::currentThread();
 thread->setPriority(QThread::LowestPriority);

 qDebug("Current thread %x. Thread set to Lowest priority",
 (quintptr)thread);
}

void setHighestPriorityFunction()
{
 QThread* thread = QThread::currentThread();
 thread->setPriority(QThread::HighestPriority);

 qDebug("Current thread x. Thread set to Highest priority",
 (quintptr)thread);
}

int calculate(const int& num)
{
#if 0 // Test once call per thread with function
 #if 0 // Set lowest thread priority
 qCallOncePerThread(setLowestPriorityFunction);
 #else // Set highest thread priority
 qCallOncePerThread(setHighestPriorityFunction);
 #endif
#else // Test once call per thread with functor
 #if 0
 qCallOncePerThread(setPriorityFunctor(QThread::HighestPriority));
 #else
 qCallOncePerThread(setPriorityFunctor(QThread::LowestPriority));
 #endif
#endif

 return ~num;
}

static QBasicAtomicInt flag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CO_Request);

int parityFilter(const int& num)
{
 qCallOnce(run_once(), flag);

 return num 2 ? false : true;
}

static QBasicAtomicInt testflag = Q_BASIC_ATOMIC_INITIALIZER(CallOnce::CO_Request);

int main(int argc, char* argv[])
{
 QCoreApplication a(argc, argv);

 QTime t; t.start();
 for (int i = 0; i < 1000 * 1000 * 100;''+i)
 qCallOnce(run_once(), testflag);
 qDebug("%d ms", t.elapsed());

QVector<int> ints;

std::generate_n(std::back_inserter(ints),
 (int)Max, inc_functor());

// Test qCallOnce
 {
 QVector<int> results = QtConcurrent::blockingMapped(ints, calculate);
 qDebug() << results;
 }

// Test qCallOncePerThread
 {
 QtConcurrent::blockingFilter(ints, parityFilter);
 qDebug() << ints;
 }

return 0;
}

Source

P.S.: This implementation faster than boost::call_once, but slower than std::call_once