I have been trying to invoke threads from class constructors to no avail. Why is it so hard to pass a function or object to a thread.
#include <iostream>
#include <thread>
class a{
public:
a();
std::thread t;
int data;
//virtual void fn();
};
void fn(a *p)
{
std::cout << "Thread Function : " << p->data << std::endl;
}
a::a():data(10)
//a::a():data(10),t(fn,this)
{
void (*fnp)(a *p) = fn;
fn(this);
fnp(this);
t(fnp, this);
}
int main ()
{
a av;
return 0;
}
Its output looks as:
preetam@preetam-GL702ZC:~/Desktop$ g++ v.cpp -lpthread
v.cpp: In constructor ‘a::a()’:
v.cpp:23:13: error: no match for call to ‘(std::thread) (void (*&)(a*), a*)’
t(fnp, this);
All I want is to start a thread from the constructor and have that thread access the class members with ease.
There are a couple of ways to do this. The one that's most similar to the code in the question is simply this:
a::a() : data(10) {
std::swap(t, std::thread(fn, this));
}
That's because threads are created in the constructor of a thread object. In this code, t
gets default-constructed because it's not mentioned in the initializer list. The code in the body of the constructor constructs a temporary thread object and swaps it with the default-constructed t
object. After the swap, t
has the thread that's running fn(this)
, and the temporary object has the default-constructed thread. At the end of the statement the temporary thread object gets destroyed.
Instead of that create-and-swap approach, you can also create the thread directly in the initializer list:
a::a() : data(10), t(fn, this) { // won't work, though...
}
The problem here is a bit subtle: despite putting the initializer for data
in front of the initializer for t
, the constructor for t
runs before data
gets initialized. So there's a data race. This happens because member objects are constructed in their order of declaration; t
is declared before data
, so t
gets constructed before data
gets initialized. Regardless of their order in the initializer list of the constructor.
So, the solution is to change the order of the declarations:
class a {
int data;
std::thread t;
a();
};
Now the previous code will work correctly.
If you're writing code like this, with an important dependency on the order of declaration of the members, you owe to future maintainers, including yourself, to document that dependency with a comment:
class a {
// IMPORTANT: data must be declared before t
int data;
std::thread t;
a();
};
Note, also, that with both of these approaches, the thread that gets launched is operating on an object whose constructor has not finished running. If the thread function uses any members of that object and the constructor modifies those members after launching the thread you've got a data race, hence, undefined behavior.
In particular, if your plan is to add virtual functions to a
and override them in derived classes, you're doomed. When the thread is launched, the base class constructor is still running, and the member initializers and body of the derived class constructor have not run. They'll run after the base class constructor, and again, you're deep into undefined behavior.