Потоки с классами и std :: packaged_task - PullRequest
0 голосов
/ 07 января 2020

Я пытаюсь реализовать этот пул потоков с классами в C ++. С тех пор я был уверен, что понимаю, как работают классы, но теперь я злюсь.

У меня есть 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, она работает нормально, но если я пытаюсь использовать функцию, определенную в класс не работает, но мне нужно иметь доступ к переменным класса.

Так что у меня много сомнений: 1) почему это не работает, что я делаю не так? 2) все в порядке, чтобы создать 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) в обоих случаях, как я могу получить доступ к будущему задачи, которую я создал?

Ответы [ 2 ]

0 голосов
/ 07 января 2020

1) Это не работает, потому что вы неправильно создаете упакованную задачу.

Поскольку вы пытаетесь использовать функцию-член, вам нужно указать, какой объект будет использоваться для вызова этих функций, чтобы вы могли попробуйте разные подходы

  • Привязать объект с помощью функции-члена
  • Использовать лямбду в качестве прокси

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

Для второй функции достаточно просто передать функцию, поскольку она является «свободной» функцией

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;
}
0 голосов
/ 07 января 2020

Как вы правильно сказали, проблема в том, что вы должны предоставить объект, для которого вы хотите вызвать функцию-член. Я вижу два решения для этого

// 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).

Я предпочитаю первое решение. Это один из многих случаев, когда лямбды гораздо проще, чем использовать bind.

...