38

これは例です:

#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

-O2フラグの有無にかかわらず、gcc 4.5.2 でコンパイルされています。

これは予想される動作ですか?

このプログラムのデータ競合はありませんか?

ありがとうございました

4

3 に答える 3

88

のコンストラクターstd::threadは引数の型を推測し、それらのコピーを値で格納します。これは、引数オブジェクトの存続期間が少なくともスレッドの存続期間と同じであることを保証するために必要です。

C++ テンプレート関数の引数の型推定メカニズムは、 typeTの引数から型を推定しますT&。へのすべての引数std::threadがコピーされてからスレッド関数に渡されるため f1()f2()常にそのコピーが使用されます。

参照の使用を主張する場合は、boost::ref()orを使用して引数をラップしstd::ref()ます。

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

または、単純にしたい場合は、ポインターを渡します。これは、舞台裏であなたのために何をするboost::ref()かです。std::ref()

于 2011-02-25T11:55:10.860 に答える
9

std::ref()これらの状況で明示的な(または)が必要であることはboost::ref()、参照を渡すことは本質的に危険なことである可能性があるため、実際には非常に便利な安全機能です。

非 const 参照ではローカル変数を渡す危険性が非常に高く、const 参照では一時的なものである可能性があり、別のスレッドで呼び出される関数を作成している場合 (および bind を使用)一般に、関数は後で/非同期的に呼び出されることが多いため、オブジェクトが有効でなくなるという大きな危険があります。

バインディングはきちんとしているように見えますが、これらのバグを見つけるのは最も困難です。なぜなら、エラーがキャッチされた場所 (つまり、関数の呼び出し時) は、エラーが発生した場所 (バインディング時) と同じではなく、作業が非常に困難になる可能性があるためです。その時点でどの関数が呼び出されているか、したがって、それがバインドされた場所を正確に出力します。

参照として渡す変数のスコープ内でスレッドに参加すると、インスタンス内で安全になります。したがって、それが事実であることがわかっている場合は、参照を渡すためのメカニズムがあります。

特に、参照によって自動的に取得した場合に破損するコピーを作成することに依存している既存のコードがおそらくたくさんあるため、変更を希望する言語の機能ではありません (その後、明示的な方法でコピーを強制します)。

于 2011-02-25T12:51:04.537 に答える
9

への参照によってパラメーターを渡したい場合は、std::threadそれぞれを で囲む必要がありますstd::ref

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

詳細はこちら

于 2011-02-25T11:53:14.130 に答える