Шаблон для будущего преобразования

В настоящее время мы очень сильно используем асинхронные значения.
Предположим, что у меня есть функция, которая делает что-то вроде этого:

int do_something(const boost::posix_time::time_duration& sleep_time)
{
BOOST_MESSAGE("Sleeping a bit");
boost::this_thread::sleep(sleep_time);
BOOST_MESSAGE("Finished taking a nap");

return 42;
}

В какой-то момент в коде мы создаем задачу, которая создает будущее для такого значения int, которое будет установлено packaged_task — вот так (worker_queue в этом примере является boost :: asio :: io_service):

boost::unique_future<int> createAsynchronousValue(const boost::posix_time::seconds& sleep)
{
boost::shared_ptr< boost::packaged_task<int> > task(
new boost::packaged_task<int>(boost::bind(do_something, sleep)));
boost::unique_future<int> ret = task->get_future();

// Trigger execution
working_queue.post(boost::bind(&boost::packaged_task<int>::operator (), task));

return boost::move(ret);
}

В другом месте кода я хочу обернуть эту функцию, чтобы вернуть объект более высокого уровня, который также должен стать будущим. Мне нужна функция преобразования, которая принимает первое значение и преобразует его в другое значение (в нашем реальном коде у нас есть несколько уровней и асинхронный RPC, который возвращает фьючерсы на ответы — эти ответы должны быть преобразованы во фьючерсы в реальные объекты, POD или даже в void будущее, чтобы иметь возможность ждать или ловить исключения). Так что это функция преобразования в этом примере:

float converter(boost::shared_future<int> value)
{
BOOST_MESSAGE("Converting value " << value.get());
return 1.0f * value.get();
}

Затем я подумал о создании ленивого будущего, как описано в документации Boost, чтобы сделать это преобразование только при желании:

void invoke_lazy_task(boost::packaged_task<float>& task)
{
try
{
task();
}
catch(boost::task_already_started&)
{}
}

И тогда у меня есть функция (может быть API более высокого уровня) для создания обернутого будущего:

boost::unique_future<float> createWrappedFuture(const boost::posix_time::seconds& sleep)
{
boost::shared_future<int> int_future(createAsynchronousValue(sleep));
BOOST_MESSAGE("Creating converter task");
boost::packaged_task<float> wrapper(boost::bind(converter, int_future));

BOOST_MESSAGE("Setting wait callback");
wrapper.set_wait_callback(invoke_lazy_task);

BOOST_MESSAGE("Creating future to converter task");
boost::unique_future<float> future = wrapper.get_future();

BOOST_MESSAGE("Returning the future");
return boost::move(future);
}

В конце я хочу использовать его следующим образом:

{
boost::unique_future<float> future = createWrappedFuture(boost::posix_time::seconds(1));
BOOST_MESSAGE("Waiting for the future");
future.wait();
BOOST_CHECK_EQUAL(future.get(), 42.0f);
}

Но здесь я получаю исключение о нарушении обещания. Причина, по-видимому, довольно ясна для меня, потому что packaged_task, который выполняет преобразование, выходит из области видимости.

Итак, мой вопрос: как мне справиться с такими ситуациями. Как я могу предотвратить уничтожение задания? Есть ли образец для этого?

Bests,

Ronny

0

Решение

Вам необходимо правильно управлять временем жизни объекта задачи.

Самый правильный способ — это вернуться boost::packaged_task<float> вместо boost::unique_future<float> от createWrappedFuture(), Вызывающий будет ответственен за получение будущего объекта и продление срока жизни задачи до тех пор, пока не будет готово будущее значение.

Или вы можете поместить объект задачи в некоторую «ожидающую» очередь (глобальную или классную) так же, как вы это делали в createAsynchronousValue, Но в этом случае вам нужно будет точно управлять временем жизни задачи и удалять ее из очереди после завершения. Поэтому не думайте, что это решение имеет преимущества перед возвратом самого объекта задачи.

0

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector