Passing Object by Reference to Std::Thread in C++11

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 or std::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 when
cout << "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



Leave a reply



Submit