Best Way to Start a Thread as a Member of a C++ Class

Best way to start a thread as a member of a C++ class?

I usually use a static member function of the class, and use a pointer to the class as the void * parameter. That function can then either perform thread processing, or call another non-static member function with the class reference. That function can then reference all class members without awkward syntax.

Is there a safe way to have a std::thread as a member of a class?

There is no better way, in general, than having a separate Start function.

Suppose MyClass is a base class for some future (unknown) class Derived. If the thread is started while (or before) the MyClass constructor runs, it always risks calling the "wrong" implementation of some virtual function overridden by Derived.

The only way to avoid this is to have the thread wait until after Derived is fully constructed, and the only way to do that is to call some other function after the Derived constructor completes to tell the thread to "go"... Which means you must have some kind of separately-invoked Go function.

You might as well just have a separately-invoked Start function instead and forego the complexity of waiting.

[Update]

Note that, for complex classes, "Two-Phase Construction" is an idiom recommended by some. Starting the thread would fit seamlessly into the "initialization" phase.

How to use a thread member variable of a class c++

Assuming that the creation of this thread object will happen in
startTask(), what is the scope of this thread object? Does it live
till the Example class object gets destroyed like normal member
variables? Or its scope is the scope of that particular
method(startTask())?

The scope of the thread object is at the level of the Example object. But, the thread will not start running until you call startTask(). Do no confuse having a thread instance and having a running thread instance.

Where should I call .join() on the spawned thread if I want this
thread to live as long as the Example object? In the destructor of
Example class?

Yes, you can call it at the destructor and it is safe.

Some
material(https://thispointer.com/c11-how-to-use-stdthread-as-a-member-variable-in-class/)
online says I should create move-only class if I need to use
std::thread as member variable, why is that?

Because the std::thread is not copyable, but it is movable. So your example class cannot be copyable as well.

Is it good to have a thread object as a member variable? Reasons?

Yes, why not. I see no problem with that. You create one class with the responsibility of managing a resource, that is a thread.

Start thread with member function

#include <thread>
#include <iostream>

class bar {
public:
void foo() {
std::cout << "hello from member function" << std::endl;
}
};

int main()
{
std::thread t(&bar::foo, bar());
t.join();
}

EDIT:
Accounting your edit, you have to do it like this:

  std::thread spawn() {
return std::thread(&blub::test, this);
}

UPDATE: I want to explain some more points, some of them have also been discussed in the comments.

The syntax described above is defined in terms of the INVOKE definition (§20.8.2.1):

Define INVOKE (f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of
    type T or a reference to an object of a type derived from T;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous
    item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t 1 is an object of type T or a

    reference to an object of type T or a reference to an object of a

    type derived from T;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t 1 is not one of the types described in the previous item;
  • f(t1, t2, ..., tN) in all other cases.

Another general fact which I want to point out is that by default the thread constructor will copy all arguments passed to it. The reason for this is that the arguments may need to outlive the calling thread, copying the arguments guarantees that. Instead, if you want to really pass a reference, you can use a std::reference_wrapper created by std::ref.

std::thread (foo, std::ref(arg1));

By doing this, you are promising that you will take care of guaranteeing that the arguments will still exist when the thread operates on them.


Note that all the things mentioned above can also be applied to std::async and std::bind.

Thread as member variable of class

Since std::thread are movable but not copyable, you can do like so:

class Test {

public:
std::thread t;
Test(std::thread&& rt) : t(std::move(rt)) {}
};

int main()
{
std::vector<Test> tests;

{
std::thread t([] {
std::cout << 1;
});
tests.push_back(Test(std::move(t)));
}

for (Test& mytest : tests)
{
mytest.t.join();
}

}

In C++, How a std::thread can call a member function without creating an object?

how td_1 and td_2 called the member function operator() of class H without an object of class H?

td_1 and td_2 does create objects of type H. Those objects are temporaries. Next, those supplied function object(which are temporaries in this case) are moved/copied into the storage belonging to the newly created thread of execution and invoked from there.

You can confirm this by adding a default constructor and move constructor inside class H as shown below:

#include<iostream>
#include<thread>

class H {
public:
void operator()(){
printf("This is H(), I take no argument\n");
}

void operator()(int x){
printf("This is H(), I received %d \n",x);
}
//default constructor
H()
{
std::cout<<"default constructor called"<<std::endl;
}
//move constructor
H(H&&)
{
std::cout<<"move constructor called"<<std::endl;
}

};

int main(){

int param = 0xD;

std::thread td_1 = std::thread(H());
std::thread td_2 = std::thread(H(),param);

td_1.join();
td_2.join();


return 0;
}

The output of the above program is:

default constructor called
move constructor called
move constructor called
default constructor called
move constructor called
move constructor called
This is H(), I take no argument
This is H(), I received 13

Create thread inside class with function from same class

foo_func is a (non-static) member function, and it needs an instance of foo on which to operate. This instance must be provided to the thread constructor. If you refer to the std::thread::thread reference page it explains what code is executed in the new thread. The relevant point is that which refers to f being a pointer to member function:

  • If f is pointer to a member function of class T, then it is called. The return value is ignored. Effectively, the following code is executed:

    • (t1.*f)(t2, ..., tN) if the type of t1 is either T, reference to T or reference to type derived from T.
    • ((*t1).*f)(t2, ..., tN) otherwise.

so it is clear that the instance is required.

Change to:

for(...) some_threads.push_back(std::thread(&foo::foo_func, this));

Simple example:

#include <iostream>
#include <thread>
#include <vector>

class foo
{
public:
void make_foo_func_threads()
{
for (int i = 0; i < 5; ++i)
some_threads.push_back(std::thread(&foo::foo_func, this));
for (auto& t: some_threads) t.join();
}

private:
void foo_func() { std::cout << "Hello\n"; }
std::vector<std::thread> some_threads;
};

int main()
{
foo f;
f.make_foo_func_threads();
}


Related Topics



Leave a reply



Submit