作为线程参数的指针和引用之间的区别
这是示例:
#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
标志。
这是预期的行为吗?
该程序是否没有数据竞争?
谢谢
This is the example:
#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;
}
And the output is:
ret=0
ret=5
Compiled with gcc 4.5.2, with and without -O2
flag.
Is this expected behavior?
Is this program data race free?
Thank you
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
std::thread
的构造函数推导参数类型并按值存储它们的副本。这是为了确保参数对象的生命周期至少与线程的生命周期相同。C++ 模板函数参数类型推导机制从
T&
类型的参数推导类型T
。std::thread
的所有参数都会被复制,然后传递给线程函数,以便f1()
和f2()
始终使用该副本。如果您坚持使用引用,请使用
boost::ref()
或std::ref()
包装参数:或者,如果您更喜欢简单性,请传递指针。这就是
boost::ref()
或std::ref()
在幕后为您所做的事情。The constructor of
std::thread
deduces argument types and stores copies of them by value. This is needed to ensure the lifetime of the argument object is at least the same as that of the thread.C++ template function argument type deduction mechanism deduces type
T
from an argument of typeT&
. All arguments tostd::thread
are copied and then passed to the thread function so thatf1()
andf2()
always use that copy.If you insist on using a reference, wrap the argument using
boost::ref()
orstd::ref()
:Or, if you prefer simplicity, pass a pointer. This is what
boost::ref()
orstd::ref()
do for you behind the scene.如果您想通过引用
std::thread
传递参数,则必须将每个参数括在std::ref
中:更多信息 此处。
If you want to pass parameters by reference to a
std::thread
you must enclose each of them instd::ref
:More info here.
在这些情况下,您需要显式
std::ref()
(或boost::ref()
),这实际上是一个非常有用的安全功能,因为传递引用可以本质上是一件危险的事情。对于非常量引用,通常存在传递局部变量的危险,对于常量引用,它可能是临时的,并且当您创建要在不同线程中调用的函数时(并且使用绑定)一般来说,通常是稍后/以异步方式调用的函数),您将面临对象不再有效的巨大危险。
绑定看起来很整洁,但这些错误是最难发现的,因为捕获错误的位置(即在调用函数时)与发生错误的位置(在绑定时)不同,并且工作起来可能非常困难确切地知道当时正在调用哪个函数,以及它的绑定位置。
当您将线程加入作为引用传递的变量的范围内时,它在您的实例中是安全的。因此,当您知道情况是这样时,就有一种传递引用的机制。
这不是我希望看到改变的语言功能,特别是因为可能有很多现有代码依赖于它来制作副本,如果它只是自动引用引用,那么副本就会中断(然后需要一种显式的方法来强制复制)。
That you are required an explicit
std::ref()
(orboost::ref()
) in these situations is actually a very useful safety feature as passing a reference can be by nature a dangerous thing to do.With a non-const reference there is quite often a danger that you are passing in a local variable, with a const-reference it might be a temporary, and as you are creating a function to be called in a different thread (and with bind in general, often a function to be called later / in an asynchronous way) you will have the big danger of the object being no longer valid.
binding looks tidy but these bugs are the hardest to find, as where the error is caught (i.e. in calling the function) is not the same place that the error was made (at the time of binding) and it can be very hard to work out exactly which function is being called at the time, and therefore where it was bound.
It is safe in your instance as you join the thread in the scope of the variable you are passing as reference. Therefore when you know that to be the case there is a mechanism for passing a reference.
It is not a feature of the language I would like to see changed, particularly as there is probably a lot of existing code relying on it making a copy that would break if it just took by reference automatically (and would then need an explicit way to force a copy).