As this code snippet does not compile, I understand that the std::thread
need callable&&
other than callable&
.
#include <iostream>
#include <cmath>
#include <thread>
#include <future>
#include <functional>
// unique function to avoid disambiguating the std::pow overload set
int f(int x, int y) { return std::pow(x,y); }
void task_thread()
{
std::packaged_task<int(int,int)> task(f);
std::future<int> result = task.get_future();
std::thread task_td(task, 2, 10); //std::move(task) works
task_td.join();
std::cout << "task_thread:\t" << result.get() << '\n';
}
int main()
{
task_thread();
}
As std::thread
need callable&&
, why this code snippet below works?
In this code snippet, f1
is a normal function, I think it's a callable&
other than callable&&
.
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
void f1(int n)
{
for (int i = 0; i < 5; i) {
std::cout << "Thread 1 executing\n";
n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void f2(int& n)
{
for (int i = 0; i < 5; i) {
std::cout << "Thread 2 executing\n";
n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
class foo
{
public:
void bar()
{
for (int i = 0; i < 5; i) {
std::cout << "Thread 3 executing\n";
n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
class baz
{
public:
void operator()()
{
for (int i = 0; i < 5; i) {
std::cout << "Thread 4 executing\n";
n;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
int n = 0;
};
int main()
{
int n = 0;
foo f;
baz b;
std::thread t1; // t1 is not a thread
std::thread t2(f1, n 1); // pass by value
std::thread t3(f2, std::ref(n)); // pass by reference
std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
std::thread t5(&foo::bar, &f); // t5 runs foo::bar() on object f
std::thread t6(b); // t6 runs baz::operator() on a copy of object b
t2.join();
t4.join();
t5.join();
t6.join();
std::cout << "Final value of n is " << n << '\n';
std::cout << "Final value of f.n (foo::n) is " << f.n << '\n';
std::cout << "Final value of b.n (baz::n) is " << b.n << '\n';
}
CodePudding user response:
std::thread
takes all of the objects it is given (the function to be called and its parameters) by "decay copy". This effectively means that a new object will be created for each parameter given. These objects will be stored by std::thread
and used when calling your function on the new thread. The new objects will be created via copy if you provide lvalues, or by move if you provide xvalues or prvalues.
This means that, by default, std::thread
will not reference variables outside of the constructor call itself. This also means that it requires explicit effort (std::ref
or similar) to access an object through a parameter which is not owned by the thread being invoked.
packaged_task
is a move-only type. The compilation failed because you did not invoke std::move
on it, so decay-copy attempted to copy the type. Since it's move-only, compilation failed. But that is a property of packaged_task
, not of std::thread
.