Home > front end >  About the parameters passed to the ctor of `std::thread`
About the parameters passed to the ctor of `std::thread`

Time:03-08

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.

  • Related