В настоящее время я использую Boost 1.55 на Windows с BOOST_THREAD_VERSION=2
, BOOST_THREAD_PROVIDES_FUTURE
, а также BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
,
В модульном тесте продолжение, похоже, ведет себя само по себе, но в реальном коде оно блокируется.
Базовая структура кода выглядит так на стороне производителя:
typedef std::map<std::string, std::string> Dictionary;
struct Operation
{
std::unique_ptr<boost::promise<Dictionary>> m_promise;
boost::future<Dictionary> Start(const Dictionary& data)
{
m_promise.reset(new boost::promise<Dictionary>());
// pass off work to something else
// it runs asynchronously then calls Complete
return m_promise->get_future();
}
void Complete(const Dictionary& result)
{
m_promise->set_value(result);
m_promise.reset();
}
};
И потребительская сторона выглядит так:
m_Operation.Start(data).then([=](boost::future<Dictionary> f)
{
// ...
DoSomething(f.get());
});
return true;
Продолжение вызывается, но f.get()
блокирует на неопределенный срок, как и set_value
,
Насколько я могу сказать, один поток заблокирован внутри promise::set_value
(в частности, в future_async_shared_state_base::join()
), а другой заблокирован в future::get()
(в частности, мьютекс в shared_state_base::wait()
).
(Похоже на нить, которая находится в set_value()
пытается уничтожить продолжение после приобретения того же мьютекса, который get()
заблокирован.)
Это ошибка, исправленная в более поздней версии Boost, я использую неподдерживаемую конфигурацию или я делаю что-то еще неправильно?
Я не хочу, чтобы что-либо вовлеченное блокировало, по любой причине. Это должен быть просто обратный вызов & сдавать.
Также я озадачен, почему продолжение выполняется в отдельном потоке, а не внутри set_value
, Кажется, что ни одна из возможных политик запуска не поддерживает синхронное выполнение продолжения, что, как я полагаю, будет естественным заданием по умолчанию (именно так в конечном итоге ведут себя задачи .NET). Я действительно предпочел бы, чтобы он не запускал новую ветку только для запуска продолжения, так как в конечном итоге это просто отправляет работу в службу Asio.
Или фьючерсы — просто неправильная модель для такого рода вещей? Вместо этого я попытался создать асинхронный метод Asio, но быстро заблудился в шаблоне.
Задача ещё не решена.
Других решений пока нет …