james
james

Reputation: 1165

std::thread constructor with no parameter

According to cppreference.com, the std::thread constructor with no parameter means:

Creates new thread object which does not represent a thread.

My questions are:

  1. Why do we need this constructor? And if we create a thread using this constructor, how can we "assign" a thread function later?
  2. Why don't we have a "run(function_address)" method so that when constructed with no parameter, we can specify a function to "run" for that thread.
  3. Or, we can construct a thread with a callable parameter (function, functors, etc.) but call a "run()" method to actually execute the thread later. Why is std::thread not designed in this way?

Upvotes: 6

Views: 10161

Answers (4)

MikeMB
MikeMB

Reputation: 21166

EDIT: Maybe lets firsts comment on the very last part:

3.2 Why std::thread is not designed in this way?

I don't know why exactly (there are surely advantages and disatvantages - see Jonathan Wakely's answer for a more details about the rational behind it), but it seems that c++11 std::thread is modelled much closer to pthreads than e.g. QT's or Java's QThread/Thread classes, which might be the source of your confusion.

As to the rest of your questions:

1.1 why do we need this constructor?

You might want to create a std::thread variable but don't directly start a thread (e.g. a class member variable or an element of a static array, es shown by alangab). It's not much different to an std::fstream that can be created without a filename.

1.2 And if we create a thread using this constructor, how can we "assign" a thread function later?

For example:

std::thread myThread;

// some other code

myThread = std::thread(foo()); 
  1. Why don't we have a "run(function_address)" method so that when constructed with no parameter, we can specify a function to "run" for that thread.

I don't know, why it was designed like this, but I don't see the benefit a run method would have compared to above syntax.

3.1 Or, we can construct a thread with a callable parameter(function, functors, etc.) but call a "run()" method to actually execute the thread later.

You can simulate this by creating a lambda or std::function object and create the thread when you want to run the function.

auto myLambda = [=]{foo(param1, param2);};
// some other code
std::thread myThread(myLambda);

If you want to use the syntax you describe, I'd recommend to write your own Thread wrapper class (should only take a few dozen lines of code) that (optionally) also ensures that the thread is either detached or joined upon destruction of the wrapper, which is - in my opinion - the main problem with std::thread.

Upvotes: 2

Jonathan Wakely
Jonathan Wakely

Reputation: 171393

Your question suggests there might be some confusion and it would be helpful to clearly separate the ideas of a thread of execution from the std::thread type, and to separate both from the idea of a "thread function".

  • A thread of execution represents a flow of control through your program, probably corresponding to an OS thread managed by the kernel.
  • An object of the type std::thread can be associated with a thread of execution, or it can be "empty" and not refer to any thread of execution.
  • There is no such concept as a "thread function" in standard C++. Any function can be run in a new thread of execution by passing it to the constructor of a std::thread object.
  1. why do we need this constructor?

To construct the empty state that doesn't refer to a thread of execution. You might want to have a member variable of a class that is a std::thread, but not want to associate it with a thread of execution right away. So you default construct it, and then later launch a new thread of execution and associate it with the std::thread member variable. Or you might want to do:

std::thread t;
if (some_condition) {
  t = std::thread{ func1, arg1 };
}
else {
  auto result = some_calculation();
  t = std::thread{ func2, arg2, result };
}

The default constructor allows the object t to be created without launching a new thread of execution until needed.

And if we create a thread using this constructor, how can we "assign" a thread function later?

You "assign" using "assignment" :-)

But you don't assign a "thread function" to it, that is not what std::thread is for. You assign another std::thread to it:

std::thread t;
std::thread t2{ func, args };
t = std::move(t2);

Think in terms of creating a new thread of execution not "assigning a thread function" to something. You're not just assigning a function, that's what std::function would be used for. You are requesting the runtime to create a new thread of execution, which will be managed by a std::thread object.

  1. Why don't we have a "run(function_address)" method so that when constructed with no parameter, we can specify a function to "run" for that thread.

Because you don't need it. You start new threads of execution by constructing a std::thread object with arguments. If you want that thread of execution to be associated with an existing object then you can do that by move-assigning or swapping.

  1. Or, we can construct a thread with a callable parameter(function, functors, etc.) but call a "run()" method to actually execute the thread later. Why std::thread is not designed in this way?

Why should it be designed that way?

The std::thread type is for managing a thread of execution not holding a callable object for later use. If you want to create a callable object that can be later run on a new thread of execution there are lots of ways to do that in C++ (using a lambda expression, or std::bind, or std::function, or std::packaged_task, or a custom functor type). The job of std::thread is to manage a thread of execution not to hold onto a callable object until you want to call it.

Upvotes: 13

alangab
alangab

Reputation: 867

The default constructor gives you then possibility to create array of threads:

thread my_threads[4];

for (int i=0; i<4; i++)
{
   thread temp(func,...);
   my_threads[i]=move(temp);
}

the thread created with default costructor "become" a "real" thread with the move costructor.

You can use thread with standard container if you need/like.

Upvotes: 2

Niall
Niall

Reputation: 30604

The default constructor is provided such that an "empty" thread object can be created. Not all thread objects will be associated with a thread of execution at the time of construction of said object. Consider when the thread is a member of some type and that type has a default constructor. Consider another case that the thread type has no concept of a "suspended" thread, i.e. it can't be created in a suspended state.

The thread type doesn't have a "run" method of some sort since the one of the original design decisions (IIRC) was to have a "strong" association between the thread object and the thread of execution. Allowing threads to be "moved" makes that intent clearer (in my opinion). Hence moving an instance of a thread object to an "empty" object is clearer than attempting to "run" a thread.

It is conceivable that you can create a wrapper class of some sort that offers the "run" method, but I think this may be a narrower use case, and that can be solved given the API of the std::thread class.

Upvotes: 4

Related Questions