类 和 std::packaged_task 的话题

Threads with Classes and std::packaged_task

我试图在 C++ 中用 classes 实现这个线程池。 从现在开始,我有信心了解 classes 是如何工作的,但现在我很生气。

我有2个文件 "JobScheduler.h" 和 "JobScheduler.cpp"

JobScheduler.h

class JobScheduler {
    int thread_id;
    std::vector<std::thread> pool;
    std::mutex m1;

    int set_id();
public:
    JobScheduler();
    ~JobScheduler();

    void Start();
};

JobScheduler.cpp

int id = 0;
std::mutex m;

JobScheduler::JobScheduler() {...}

JobScheduler::~JobScheduler() {...}

int JobScheduler::set_id() {
    m1.lock();
    int tmp_id = thread_id;
    thread_id++;
    std::cout << "id = " << tmp_id << "\n";
    m1.unlock();
    return tmp_id;;
}

int set_id_02(){
    m.lock();
    int tmp_id = id;
    id++;
    std::cout << "id = " << tmp_id << "\n";
    m.unlock();
    return tmp_id;
}

void JobScheduler::Start(){
    // THIS DOESN'T WORK
    /*
    for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
        pool.emplace_back(std::thread(std::packaged_task<void()>(JobScheduler::set_id))); // <--- error 
    }

    ... // print something and join threads
    */

    // MANY THREADS - NO CLASS METHOD AS FUNCTION AND GLOBAL CPP VARIABLE - WORK
    /*
    for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
        pool.emplace_back(std::thread(std::packaged_task<int()>(set_id_02)));
    }

    ... // print something and join threads
    */
}

现在,如果我使用 .cpp 中定义的函数,它可以正常工作,但如果我尝试使用我在 class 中定义的函数,它就不起作用,但我需要能够访问 Class变量。

所以我有很多疑惑: 1)为什么这不起作用,我错了什么? 2) 可以像我在 for? 中那样创建 std::package_task 吗?或者我应该做类似

的事情
std::pakaged_task<int()> main_task(set_id);
for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
    pool.emplace_back(std::thread(main_task));
}

3) 在这两种情况下,如何访问我创建的任务的未来?

正如您所说的那样,问题在于您必须提供要在其上调用成员函数的对象。我看到了两个解决方案

// 1st wrap in a lambda
for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
    pool.emplace_back(std::thread(std::packaged_task<void()>([this](){this->set_id();}))); 
}

// 2nd Use std::mem_fn and std::bind
for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
    pool.emplace_back(std::thread(std::packaged_task<void()>(std::bind(std::mem_fn(&JobScheduler::set_id), *this)))); 
}

我想第一个应该清楚了。在第二个中,std::mem_fn 创建一个函数 f 使得 f(object) 执行 object.set_id() 并且 std::bind 创建一个函数 g 使得 g() f(this).

我更喜欢第一种方案。这是 lambda 比使用 bind.

简单得多的许多情况之一

1) 没有成功,因为你创建的打包任务有误

由于您正在尝试使用成员函数,因此您必须指定将使用哪个对象来调用这些函数,以便您可以尝试不同的方法

  • 用成员函数绑定对象
  • 使用 lambda 作为代理

    std::packaged_task<int()>(std::bind(&JobScheduler::set_id, this))
    std::packaged_task<int()>([this]{ return set_id(); })
    

因为第二个函数是 "free" 函数,所以只传递函数就足够了

std::packaged_task<int()>(set_id_02);

2) 见上文

3) 为了访问您的 packaged_task 结果,您必须存储它的未来

std::vector<std::future<int>> results;
for(unsigned int i = 0; i < std::thread::hardware_concurrency(); i++){
  auto task = std::packaged_task<int()>([this]{ return set_id(); });
  results.emplace_back(task.get_future());
  pool.emplace_back(std::thread(std::move(task)));
}

//Access results

for (auto& f : results) {
  cout << f.get() << endl;
}