1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
| // Fichier: main.cpp
#include <iostream>
#include <sstream>
#include <string>
#include <QtCore>
#include <QtDebug>
using namespace std;
// On fournit une façon thread-safe d'écrire sur la console.
// (l'écho fournit par cin n'est *pas* concerné par l'aspect threadsafe!)
class LoggingApplication : public QCoreApplication
{
public:
LoggingApplication(int argc, char **argv)
:QCoreApplication(argc, argv)
{
}
void log(std::string str)
{
QMutexLocker locker(&outMutex);
std::cout << str;
}
private:
QMutex outMutex;
};
// Cette classe représente une tache; la tache est représentée par une durée aléatoire.
// Le résultat obtensible est la durée convertit en QString (1->a, 2->b, etc...).
class Task : public QObject, public QRunnable
{
Q_OBJECT
public:
Task(int taskIdx, int timeToFillTask)
:taskIndex(taskIdx), taskDuration(timeToFillTask)
{
}
// Cette fonction sera exécutée par l'un des thread de la thread pool globale.
virtual void run()
{
QTime startTime(0, 0);
startTime.start();
ostringstream oss;
oss << "Runnable: Starting task #" << taskIndex << " (duration: "
<< taskDuration << " ms)\n";
LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
app->log(oss.str());
while (startTime.elapsed() < taskDuration)
{
// on ne fait rien, on simule juste l'exécution d'une tâche de longueur variable
}
if (taskDuration > 1000)
result = (char)('a' + (taskDuration / 1000));
if (taskDuration > 100)
result += (char)('a' + ((taskDuration / 100) % 10));
if (taskDuration > 10)
result += (char)('a' + ((taskDuration / 10) % 10));
if (taskDuration > 0)
result += (char)('a' + (taskDuration % 10));
emit finished(taskIndex);
}
QString getResult() const
{
return result;
}
signals:
void finished(int taskIdx);
private:
int taskIndex;
int taskDuration;
QString result;
};
class MasterThread : public QThread
{
Q_OBJECT
public:
const int maxTaskLength;
const int minTaskLength;
public:
MasterThread()
:maxTaskLength(3000), minTaskLength(500), initialTaskCount(10), taskFinishedCount(0)
{
for (int i = 0; i < initialTaskCount; i++)
{
int duration = qrand() / (RAND_MAX / (maxTaskLength - minTaskLength) + 1);
duration += minTaskLength;
addTask(duration, false);
}
}
void addTask(int taskDuration, bool startASAP)
{
Task *t = new Task(tasks.size(), taskDuration);
// on veut récupérer le résultat à la fin de l'exécution de la tâche,
// donc le thread pool ne doit pas le supprimer
t->setAutoDelete(false);
tasks.push_back(t);
bool b = connect(t, SIGNAL(finished(int)), this, SLOT(onTaskFinished(int)), Qt::QueuedConnection);
if (!b)
{
LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
ostringstream oss;
oss << "Failed connection of task #" << tasks.size() << "\n";
app->log(oss.str());
}
if(startASAP)
{
QThreadPool *threadPool = QThreadPool::globalInstance();
threadPool->start(t);
}
}
protected:
virtual void run()
{
// On commence toutes les tâches préprogrammées
QThreadPool *threadPool = QThreadPool::globalInstance();
threadPool->setMaxThreadCount(4); // On demande 4 tâches à la fois
foreach(Task *t, tasks)
threadPool->start(t);
// L'event loop tourne pour laisser les signaux des tâches atteindre ce thread
// tant que quit() n'a pas été appelé
LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
app->log("Master thread: enter event loop\n");
exec();
app->log("Master thread leave event loop\n");
// Une demande d'arrêt a été effectuée, on attend le thread pool.
// (Toutes les tâches en queues sont exécutées avant le retour de cette fonction.)
threadPool->waitForDone();
app->log("Master thread: threadpool finished its queue\n");
// Ici, un nombre négatif peut être affiché si on a quitté le programme
// avant la fin de l'exécution de chacune des tâches.
ostringstream oss;
oss << "Master thread: Finished executing " << taskFinishedCount << " tasks ("
<< taskFinishedCount - initialTaskCount << " supplementary tasks performed)\n";
app->log(oss.str());
}
public slots:
void onTaskFinished(int taskIdx)
{
ostringstream oss;
oss << "Master thread: Task #" << taskIdx << " finished. Result: "
<< tasks[taskIdx]->getResult().toStdString() << "\n";
LoggingApplication *app = (LoggingApplication*)QCoreApplication::instance();
app->log(oss.str());
delete tasks[taskIdx];
tasks[taskIdx] = 0;
taskFinishedCount++;
}
private:
int initialTaskCount;
QVector<Task*> tasks;
int taskFinishedCount;
};
// Ce thread va juste rester à l'écoute de stdin, jusqu'à saisie
// d'un nombre négatif qui l'arrête (et entraîne l'arrêt de l'application).
class InputThread : public QThread
{
public:
InputThread(MasterThread &master)
:m(master)
{
}
protected:
virtual void run()
{
int i;
while (cin >> i)
{
if(i > 0)
{
int duration = qrand() / (RAND_MAX / m.maxTaskLength - m.minTaskLength + 1);
duration += m.minTaskLength;
m.addTask(duration, true);
}
else
{
cout << "Input thread: Request exit\n";
m.quit(); // On demande un arrêt du thread
cout << "Input thread: Wait for master thread\n";
m.wait();
break;
}
}
}
private:
MasterThread &m;
};
#include "main.moc"
int main(int argc, char **argv)
{
LoggingApplication app(argc, argv);
qsrand(42);
// NB: ici on passe par les entrées/sorties standard qui ne sont pas thread-safe.
// conséquence possible: on peut avoir la sortie d'une tâche qui apparaît pendant qu'on tape un chiffre.
cout << "Un chiffre superieur à 0 cree une nouvelle tache; tout autre chiffre quitte:" << endl;
MasterThread master;
master.start();
InputThread input(master);
input.start();
app.connect(&input, SIGNAL(finished()), &app, SLOT(quit()));
app.exec();
} |
Partager