Passing object by reference to std::thread in C++11
Explicitly initialize the thread with a reference_wrapper
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
orstd::cref
).
Thread-safety with C++11 and passing by reference with temporary object
The temporary object is released per the standard, leading to undefined behavior. An implementation may do anything, including keeping the object's bytes in stack memory until they are overwritten, which allows your code to (incorrectly) work.
When I disassembled the binary produced by my compiler (clang++ 9.0.1) I noticed that the stack pointer was not "regressed" when the block containing data
ended, thus preventing it from being overwritten whencout << "main loop" << endl;
resulted in function calls.
Furthermore, due to short string optimization, the actual ASCII "123" is stored within the std::string
object itself and not in a heap-allocated buffer.
The draft standard says the following:
6.6.4.3 Automatic storage duration
Block-scope variables not explicitly declared static, thread_local, or extern have automatic storage duration. The storage for these entities lasts until the block in which they are created exits.
Experimentally, if I make the string long enough to disable short string optimization, the program still silently works since the bytes in the buffer happen to remain intact in my experiment. If I enable ASAN, I get a correct heap use-after-free warning, because the bytes were freed at the end of the string's lifetime, yet were accessed through an illegal use of a pointer to the now-destructed string.
Passing object by reference and Multithreading
while (true) {
std::thread t1(thread_task, b);
t1.join();
}
Two things you need to know here:
- Use
std::ref
to pass a reference. - Infinite loop is Undefined Behavior in C++;
Working Example below:
#include <iostream>
#include <thread>
class Exmaple {
private:
int counter;
public:
Exmaple() {
counter = 0;
}
void doSomthing(){
counter++;
}
void print() {
std::cout << "value from A: " << counter << std::endl;
}
};
// notice that the object is passed by reference
void thread_task(Exmaple& o) {
o.doSomthing();
o.print();
}
int main()
{
Exmaple b;
for(int i =0; i < 10; i++) {
std::thread t1(thread_task, std::ref(b));
t1.join();
}
return 0;
}
Output:
value from A: 1
value from A: 2
value from A: 3
value from A: 4
value from A: 5
value from A: 6
value from A: 7
value from A: 8
value from A: 9
value from A: 10
See it Live.
Though going further you should also consider data race
C++11 threads compilation error in passing string as reference of copy
std::thread
keeps a copy of the objects that it will pass to the thread function, and when it starts the new thread it passes those arguments to the thread as rvalues. Non-const lvalue-references references cannot bind to rvalues, so the parameter for your hello
function cannot be bound to the object std::thread
tries to pass to it.
If you want to avoid this copying behavior, use std::reference_wrapper
:
int main()
{
cout << "main thread created" << endl;
string s = "HEY";
thread t(hello, std::ref(s));
t.join();
cout << s << endl;
return 0;
}
std::reference_wrapper<T>
is an object that holds a reference to an object, and, when copied, copies only the reference. It also has an implicit conversion to T&
, so when std::thread
passes the std::reference_wrapper<std::string>
object to your hello
function, it will be implicitly converted to a reference to the original object used to construct it in your main thread.
C++ Making a thread and passing Object by reference
Orders customerOrders();
declares a function. This is known as the most-vexing parse.
You can simply use Orders customerOrders;
Reference at std::thread parameters
Your f()
function does not work as you expect without std::cref()
.
Although f()
does not intent to change the value behind x
, it does not mean that the value behind this reference cannot be mutated elsewhere.
In this example, without std::cref()
a copy of the original int
is put in the thread stack, and x
references this copy; we see 1
and 1
.
On the other hand, with std::cref()
, x
still references the original; we see 1
and 2
.
/**
g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
-pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
-g -O0 -UNDEBUG -fsanitize=address,undefined -pthread
**/
#include <iostream>
#include <thread>
using namespace std::chrono_literals;
void
f(const int &x)
{
std::cout << "x=" << x << '\n';
std::this_thread::sleep_for(1000ms);
std::cout << "x=" << x << '\n';
}
int
main()
{
int i=1;
// std::thread th{f, i}; // copy to thread stack
std::thread th{f, std::cref(i)}; // reference the original i
std::this_thread::sleep_for(500ms);
i+=1;
th.join();
return 0;
}
Passing class object into thread
Use std::ref
to pass by a reference. Namely:
thread t(thread_func, std::ref(r));
Learn about std::reference_wrapper
to find out why: http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper.
Passing reference to object in callback function to std::thread
So here you have a working example:
#include <thread>
#include <functional>
class RPiServer;
void commands_conn_handler(int socket, RPiServer &server) {
// Not important code about handling connection
}
class RPiServer {
public:
void Accept(void (*acceped_conn_handler)(int, RPiServer&)) {
// (...)
int remote_socket = 0; // Doesn't matter - example.
std::thread conn_handler_thread(acceped_conn_handler, remote_socket, std::ref(*this));
conn_handler_thread.join();
}
};
int main() {
RPiServer commands_server;
commands_server.Accept(commands_conn_handler);
}
The error you was getting was because you were not providing correct type for the constructor of conn_handler_thread
. To explicitly get a reference to an object (which you need to do here), use a std::ref()
function.
P.S.: Also you copy pasted your code example wrong, duplicating the void Accept
part. You also had a most vexing parse error in main()
.
Related Topics
Are Inline Virtual Functions Really a Non-Sense
Initializing a Static Std::Map≪Int, Int≫ in C++
Std::Unique_Ptr With an Incomplete Type Won't Compile
How to List the Symbols in a .So File
C/C++ Macro String Concatenation
How to Use Enums as Flags in C++
Dynamically Load a Function from a Dll
Rvalue Reference Is Treated as an Lvalue
Finding the Type of an Object in C++
How to Enable C++17 Compiling in Visual Studio
What Is the Usefulness of 'Enable_Shared_From_This'
Why Not Non-Const Reference to Temporary Objects
Why Pass by Const Reference Instead of by Value
"Undefined Reference To" Template Class Constructor
How to Get and Use the Header File ≪Graphics.H≫ in My C++ Program