Le code de cet exemple est naïf, mais vise à illustrer la différence entre deux programmes, l'un bloquant dû à des entrées/ sorties et et l'autre non-bloquant malgré des entrées/ sorties. Il utilise les outils de C++ 17, mais vous pouvez demander à votre chic prof quoi faire si vous utilisez un compilateur plus ancien.
Le programme suivant décrit une tâche bloquante :
#include <iostream>
#include <string>
using namespace std; // ne faites pas ça dans un .h!!!
class erreur_lecture {};
string lire_nom() {
if (string nom; cin >> nom)
return nom;
throw erreur_lecture {};
}
int main() {
cout << "Quel est votre nom? ";
try {
auto nom = lire_nom();
cout << "Bonjour, " << nom << '\n';
} catch(erreur_lecture&) {
cerr << "Hum, il semble y avoir eu un probleme lors de la saisie de votre nom...\n";
}
}
Le côté bloquant de cette tâche tient à la lecture au clavier, dans la fonction lire_nom(), pour laquelle le temps maximal d'une exécution n'a pas de borne supérieure : cette opération dépend de circonstances externes à l'algorithme lui-même. Puisque le programme est monoprogrammé (un seul fil d'exécution), son exécution entière devient bloquante de par l'action de cette opération.
Pour sa part, le programme suivant ne bloquera pas, même si l'une de ses fonctions sera bloquée par une entrée au clavier :
#include <iostream>
#include <string>
#include <future>
#include <thread>
#include <chrono>
using namespace std; // ne faites pas ça dans un .h!!!
using namespace std::chrono; // idem!!!
class erreur_lecture {};
string lire_nom() {
if (string nom; cin >> nom)
return nom;
throw erreur_lecture {};
}
auto tache_significative(system_clock::time_point t0 = system_clock::now()) {
auto t1 = system_clock::now();
if (t1 - t0 < 500ms)
return t0;
cout << '.' << flush;
return t1;
}
int main() {
cout << "Quel est votre nom? ";
try {
auto nom = async(launch::async, lire_nom);
for(auto t = tache_significative();
nom.wait_for(10ms) != future_status::ready;
t = tache_significative(t))
;
cout << "Bonjour, " << nom.get() << '\n';
} catch(erreur_lecture&) {
cerr << "Hum, il semble y avoir eu un probleme lors de la saisie de votre nom...\n";
}
}
Dans cet exemple, bien que l'opération de lecture au clavier soit bloquante, celle-ci est logée dans un fil d'exécution distinct et le reste du programme demeure capable de réaliser une tache_significative().