Avec les threads win32, j'ai la fonction directe GetExitCodeThread()
qui me donne la valeur renvoyée par la fonction thread. Je cherche quelque chose de similaire pour std::thread
(ou booster les discussions)
Si je comprends bien, cela peut être fait avec les futurs, mais comment exactement?
Voir ce tutoriel vidéo sur les futurs C++ 11.
Explicitement avec les fils et les futurs:
#include <thread>
#include <future>
void func(std::promise<int> && p) {
p.set_value(1);
}
std::promise<int> p;
auto f = p.get_future();
std::thread t(&func, std::move(p));
t.join();
int i = f.get();
Ou avec std::async
(wrapper de niveau supérieur pour les threads et les futures):
#include <thread>
#include <future>
int func() { return 1; }
std::future<int> ret = std::async(&func);
int i = ret.get();
Je ne peux pas dire si cela fonctionne sur toutes les plates-formes (il semble fonctionner sur Linux, mais ne fonctionne pas pour moi sur Mac OSX avec GCC 4.6 .1).
Je dirais:
#include <thread>
#include <future>
int simplefunc(std::string a)
{
return a.size();
}
int main()
{
auto future = std::async(simplefunc, "hello world");
int simple = future.get();
return simple;
}
Notez que l'async propage même toutes les exceptions levées à partir de la fonction de thread
En utilisant des threads C++ 11, on ne peut pas obtenir la valeur de retour en tant que sortie de thread, ce qui était le cas avec pthread_exit(...)
Vous devez utiliser C++ 11 Future<>
Pour obtenir la valeur de retour. L'avenir est créé à l'aide d'un argument basé sur un modèle où le modèle prend la valeur de retour (intégré aux types définis par l'utilisateur).
Vous pouvez récupérer la valeur dans un autre thread en utilisant la fonction future<..>::get(..)
.
l'un des avantages de l'utilisation de future<..>
est que vous pouvez vérifier la validité de la valeur de retour, c'est-à-dire que si elle est déjà prise, vous évitez d'appeler accidentellement get()
en vérifiant la validité à l'aide de la fonction future<..>::isValid(...)
.
Voici comment vous allez écrire le code.
#include <iostream>
#include <future>
using namespace std;
auto retFn() {
return 100;
}
int main() {
future<int> fp = async(launch::async, retFn);
if(fp.valid())
cout<<"Return value from async thread is => "<<fp.get()<<endl;
return 0;
}
il convient également de noter que nous pouvons obtenir l'exécution future sur le même thread en utilisant l'option launch::deferred
comme
future<int> fp = async(launch::deferred, retFn);
Voici un exemple plus spécifique.
Une fonction simule le téléchargement avec un paramètre de rappel pour afficher la progression et annuler le téléchargement.
namespace __HeavyWork
{
int SimulateDownload(std::function<int(int)> dlCallBack)
{
for (size_t i = 0; i < 100; i++)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
if (dlCallBack(i) == -1)
{
return i;
}
}
return 100;
}
}
Nous voulons obtenir l'état du téléchargement,
#include <thread>
#include <future>
void test()
{
auto simulateCancellation = []()->bool {
static bool b = true;
if (b)
{
srand((unsigned int)time(NULL));
b = false;
}
return (Rand() % 7) == 0;
};
auto funDLCallback = [&](int i)->int {
if (simulateCancellation())
{
return -1;
}
cout << "download: " << i << endl;
return i;
};
auto funDownload = [&](std::promise<int> && p) {
p.set_value(__HeavyWork::SimulateDownload(funDLCallback));
};
std::promise<int> p;
auto f = p.get_future();
std::thread t(funDownload, std::move(p));
//dlg.doModal();
t.join();
cout << "return value: " << f.get() << endl;
}
Passez une référence/un pointeur au thread avec std::ref
async
est juste mieux que cela, mais juste pour la science, cela peut être fait:
void myfunc_reference(int& i);
std::thread(myfunc_reference, std::ref(output));
Je soupçonne que l'implémentation de async
doit faire quelque chose dans ce sens sous le capot pour nous, ce qui est essentiellement ce que vous devez faire dans le backend pthread
: Comment retourner une valeur des threads pthread en C?
Vous devez bien sûr vous assurer que la durée de vie variable dure jusqu'au retour du thread.
L'exemple de code exécutable suivant compare async et cette pire méthode:
main.cpp
#include <cassert>
#include <future>
#include <iostream>
#include <thread>
#include <vector>
int myfunc(int i) {
return i + 1;
}
void myfunc_reference(int& i) {
i = myfunc(i);
}
int main() {
unsigned int nthreads = 4;
std::vector<int> inputs{1, 2, 3, 4};
std::vector<int> outputs_expect{2, 3, 4, 5};
// future and sync. Nirvana. When you are not fighting to death with types:
// https://stackoverflow.com/questions/10620300/can-stdasync-be-use-with-template-functions
{
std::vector<std::future<int>> futures(nthreads);
std::vector<int> outputs(nthreads);
for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
futures[i] = std::async(
myfunc,
inputs[i]
);
}
for (decltype(futures)::size_type i = 0; i < nthreads; ++i) {
outputs[i] = futures[i].get();
}
assert(outputs_expect == outputs);
}
// Reference arguments.
//
// Annoying because requires:
//
// - wrapping the return function to accept references
// - keeping an array of outputs
// - std::ref
{
std::vector<std::thread> threads(nthreads);
std::vector<int> inouts(inputs);
for (decltype(threads)::size_type i = 0; i < nthreads; ++i) {
threads[i] = std::thread(myfunc_reference, std::ref(inouts[i]));
}
for (auto& thread : threads) {
thread.join();
}
assert(outputs_expect == inouts);
}
}
Compilez et exécutez avec:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp -pthread
./main.out
Testé dans Ubuntu 19.04.