Разница между указателем и ссылкой в ​​качестве параметра потока - PullRequest
36 голосов
/ 25 февраля 2011

Это пример:

#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;   
}

И вывод:

ret=0
ret=5

Скомпилировано с gcc 4.5.2, с флагом -O2 и без него.

Это ожидаемое поведение?

Свободна ли эта гонка данных программы?

Спасибо

Ответы [ 3 ]

83 голосов
/ 25 февраля 2011

Конструктор std::thread выводит типы аргументов и сохраняет их копии по значению. Это необходимо для обеспечения того, чтобы время жизни объекта аргумента было как минимум таким же, как и у потока.

Механизм вывода типа аргумента шаблонной функции C ++ выводит тип T из аргумента типа T&. Все аргументы std::thread копируются и затем передаются в функцию потока, так что f1() и f2() всегда используют эту копию.

Если вы настаиваете на использовании ссылки, оберните аргумент, используя boost::ref() или std::ref():

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

Или, если вы предпочитаете простоту, передайте указатель. Это то, что boost::ref() или std::ref() делают для вас за сценой.

9 голосов
/ 25 февраля 2011

То, что вам требуется явное std::ref() (или boost::ref()) в этих ситуациях, на самом деле является очень полезной функцией безопасности, так как передача ссылки может быть опасной по своей природе.

Снеконстантные ссылки довольно часто существует опасность, что вы передаете локальную переменную, при использовании const-ссылки она может быть временной, и когда вы создаете функцию, которая вызывается в другом потоке (и вообще с помощью bind)(часто это функция, вызываемая позже / асинхронно), вы рискуете, что объект больше не будет действительным.

привязка выглядит аккуратно, но эти ошибки труднее всего найти, например, где ошибкаперехватывается (т. е. при вызове функции) - это не то же самое место, где была сделана ошибка (во время привязки), и может быть очень трудно точно определить, какая функция вызывается в данный момент и, следовательно, где она былаbound.

Это безопасно в вашем случае, когда вы присоединяетесь к потоку в области действия переменной, которую вы передаетеСсылкаПоэтому, когда вы знаете, что в этом случае есть механизм для передачи ссылки.

Это не та особенность языка, которую я хотел бы видеть измененной, особенно потому, что, вероятно, много существующего кода полагаетсяпри этом создание копии, которая сломалась бы, если бы она просто взяла по ссылке автоматически (и тогда потребовался бы явный способ принудительного копирования).

7 голосов
/ 25 февраля 2011

Если вы хотите передать параметры по ссылке на std::thread, вы должны заключить каждый из них в std::ref:

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

Подробнее здесь .

...