Используйте функцию-член в std::packaged_task
То, что я хочу сделать, должно быть довольно легко, но я не понимаю...
Все, что я хочу сделать, - это запустить функцию-член класса в фоновом режиме в определенный момент времени. Результат этой функции также должен быть доступен "извне". Поэтому я хочу подготовить задачу в конструкторе (установив будущую переменную,...) и запустить ее на более позднем этапе.
Я пытался скомбинировать std::(packaged_task|async|future), но я не получил его на работу.
Этот фрагмент не будет компилироваться, но я думаю, что он показывает, что я хочу сделать:
class foo {
private:
// This function shall run in background as a thread
// when it gets triggered to start at some certain point
bool do_something() { return true; }
std::packaged_task<bool()> task;
std::future<bool> result;
public:
foo() :
task(do_something), // yes, that's wrong, but how to do it right?
result(task.get_future())
{
// do some initialization stuff
.....
}
~foo() {}
void start() {
// Start Task as asynchron thread
std::async as(std::launch::async, task); // Also doesn't work...
}
// This function should return the result of do_something
bool get_result() { return result.get(); }
};
Заранее спасибо!
2 ответа
Просто используйте std::bind()
:
#include <functional> // For std::bind()
foo() :
task(std::bind(&foo::do_something, this)),
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
result(task.get_future())
{
// ...
}
Более того, здесь вы делаете не то:
std::async as(std::launch::async, task)
// ^^
// Trying to declare a variable?
Так как вы хотите позвонить std::async()
функция, чтобы не объявлять объект (несуществующего) типа std::async()
, В качестве первого шага измените это на:
std::async(std::launch::async, task)
Однако обратите внимание, что этого будет недостаточно для асинхронного выполнения задачи: из-за странного поведения std::async()
когда возвращенное будущее отбрасывается, ваша задача всегда будет выполняться так, как если бы вы запустили ее синхронно - деструктор возвращенного будущего объекта будет блокироваться до завершения операции. (*)
Чтобы решить эту последнюю проблему, вы можете держать возвращенное будущее в своем result
переменная-член (вместо присвоения result
будущее вернулось std::packaged_task::get_future()
при строительстве):
result = std::async(std::launch::async, task);
// ^^^^^^^^
(*) Я думаю, что MSVC игнорирует эту спецификацию и фактически выполняет задачу асинхронно. Так что если вы работаете с VS2012, вы можете не страдать от этой проблемы.
РЕДАКТИРОВАТЬ:
Как правильно отметил Преториан в своем ответе, вышеизложенное все равно будет проблематичным, поскольку копия packaged_task
будет предпринята попытка в какой-то момент в рамках реализации async()
, Чтобы обойти эту проблему, вы заверните task
объект в ссылочной обертке с помощью std::ref()
,
do_something()
является функцией-членом, что означает, что она принимает неявное this
указатель в качестве первого аргумента. Вам нужно будет bind
this
указатель или создать лямду, которая вызывает do_something
,
foo() :
task(std::bind(&foo::do_something, this)),
result(task.get_future())
{}
или же
foo() :
task([this]{ return do_something(); }),
result(task.get_future())
{}
std::async as(std::launch::async, task);
std::async
это шаблон функции, а не тип. Таким образом, очевидное изменение
std::async(std::launch::async, task);
Но это вызывает еще одну ошибку, потому что где-то в кишечнике этого вызывают копию task
попытка, но std::packaged_task
имеет конструктор удаленных копий. Вы можете исправить это, используя std::ref
, что позволит избежать копирования.
std::async(std::launch::async, std::ref(task));