web-dev-qa-db-fra.com

Différence entre le pointeur et la référence comme paramètre de thread

Voici l'exemple:

#include<iostream>
#include<thread>
using namespace std;

void f1(double& ret) {
   ret=5.;
}

void f2(double* ret) {
   *ret=5.;
}

int main() {
   double ret=0.;
   thread t1(f1, ret);
   t1.join();
   cout << "ret=" << ret << endl;
   thread t2(f2, &ret);
   t2.join();
   cout << "ret=" << ret << endl;   
}

Et la sortie est:

ret=0
ret=5

Compilé avec gcc 4.5.2, avec et sans -O2 drapeau.

Est-ce un comportement attendu?

Cette course aux données du programme est-elle gratuite?

Je vous remercie

38
Predrag

Le constructeur de std::thread Déduit les types d'arguments et en stocke des copies par valeur. Cela est nécessaire pour garantir que la durée de vie de l'objet argument est au moins la même que celle du thread.

Le mécanisme de déduction du type d'argument de la fonction de modèle C++ déduit le type T d'un argument de type T&. Tous les arguments de std::thread Sont copiés puis passés à la fonction de thread afin que f1() et f2() utilisent toujours cette copie.

Si vous insistez pour utiliser une référence, encapsulez l'argument en utilisant boost::ref() ou std::ref():

thread t1(f1, boost::ref(ret));

Ou, si vous préférez la simplicité, passez un pointeur. C'est ce que boost::ref() ou std::ref() font pour vous derrière la scène.

86
Maxim Egorushkin

Si vous souhaitez passer des paramètres par référence à un std::thread vous devez inclure chacun d'eux dans std::ref:

thread t1(f1, std::ref(ret));

Plus d'informations ici .

9
Matteo Italia

Le fait que vous ayez besoin d'une std::ref() (ou boost::ref()) explicite dans ces situations est en fait une fonction de sécurité très utile car le passage d'une référence peut être par nature une chose dangereuse à faire.

Avec une référence non const, il y a assez souvent un danger que vous passiez dans une variable locale, avec une référence const, il peut être temporaire, et lorsque vous créez une fonction à appeler dans un thread différent (et avec bind en général, souvent une fonction à appeler plus tard/de manière asynchrone) vous aurez le gros danger que l'objet ne soit plus valide.

la liaison semble ordonnée mais ces bogues sont les plus difficiles à trouver, car où l'erreur est interceptée (c'est-à-dire lors de l'appel de la fonction) n'est pas le même endroit où l'erreur a été commise (au moment de la liaison) et il peut être très difficile de travailler exactement quelle fonction est appelée à l'époque, et donc où elle était liée.

Il est sûr dans votre instance lorsque vous joignez le thread dans la portée de la variable que vous passez comme référence. Par conséquent, lorsque vous savez que c'est le cas, il existe un mécanisme pour transmettre une référence.

Ce n'est pas une caractéristique du langage que j'aimerais voir changer, d'autant plus qu'il y a probablement beaucoup de code existant qui dépend de lui pour faire une copie qui se briserait si elle prenait juste par référence automatiquement (et aurait alors besoin d'un moyen explicite pour forcer une copie).

9
CashCow