在 C + + 11中通过引用 std: : 线程传递对象

为什么在创建 std::thread时不能通过引用传递对象?

例如,下面的代码片段提供了一个编译错误:

#include <iostream>
#include <thread>


using namespace std;


static void SimpleThread(int& a)  // compile error
//static void SimpleThread(int a)     // OK
{
cout << __PRETTY_FUNCTION__ << ":" << a << endl;
}


int main()
{
int a = 6;


auto thread1 = std::thread(SimpleThread, a);


thread1.join();
return 0;
}

错误:

In file included from /usr/include/c++/4.8/thread:39:0,
from ./std_thread_refs.cpp:5:
/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<void (*(int))(int&)>’:
/usr/include/c++/4.8/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(int&); _Args = {int&}]’
./std_thread_refs.cpp:19:47:   required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/4.8/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
_M_invoke(_Index_tuple<_Indices...>)
^

我已经更改为传递指针,但是还有更好的方法吗?

57728 次浏览

Explicitly initialize the thread with a ABC0 by using std::ref:

auto thread1 = std::thread(SimpleThread, std::ref(a));

(or std::cref instead of std::ref, as appropriate). Per notes from cppreference on std:thread:

The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with std::ref or std::cref).

Based on this comment, this answer elaborates on the reason why the arguments are not passed by reference to the thread function by default.

Consider the following function SimpleThread():

void SimpleThread(int& i) {
std::this_thread::sleep_for(std::chrono::seconds{1});
i = 0;
}

Now, imagine what would happen if the following code compiled (it does not compile):

int main()
{
{
int a;
std::thread th(SimpleThread, a);
th.detach();
}
// "a" is out of scope
// at this point the thread may be still running
// ...
}

The argument a would be passed by reference to SimpleThread(). The thread may still be sleeping in the function SimpleThread() after the variable a has already gone out of scope and its lifetime has ended. If so, i in SimpleThread() would actually be a dangling reference, and the assignment i = 0 would result in undefined behaviour.

By wrapping reference arguments with the class template std::reference_wrapper (using the function templates std::ref and std::cref) you explicitly express your intentions.

don't pass by reference if your object is stack based allocated, pass by pointer to the new object created in the call of thread API. such object will live as long as thread but should be explicitly deleted before the thread termination.

example:

void main(){
...
std::string nodeName = "name_assigned_to_thread";
std::thread nodeThHandle = std::thread(threadNODE, new std::string(nodeName));
...
}


void threadNODE(std::string *nodeName){
/* use nodeName everywhere but don't forget to delete it before the end */
delete nodeName;
}

std::thread copy(/move) its arguments, you might even see the note:

The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g., with std::ref or std::cref).

So, you might use std::reference_wrapper through ABC1/std::cref:

auto thread1 = std::thread(SimpleThread, std::ref(a));

or use lambda:

auto thread1 = std::thread([&a]() { SimpleThread(a); });