Один из сценариев для функциональных возможностей “, когда_any” - это когда мы излишне связываемся с некоторым количеством потенциально ненадежных веб-сервисов. Мало того, что они могут быть медленными, любой из них может привести к неудаче, а не к желаемому результату.
В таком случае wait_first_outcome()
не является правильным подходом. Если одна из служб быстро допускает ошибку, а другая дает реальный ответ, мы не хотим отдавать предпочтение ошибке только потому, что она появилась первой!
Учитывая unbounded_channel< T > > >
мы уже создали для wait_first_outcome()
, хотя мы можем легко переделать функцию интерфейса, чтобы получить первый успешный результат.
Возникает вопрос: что, если все функции задачи бросают исключение? В таком случае нам, наверное, лучше об этом знать.
Проект технического задания на параллелизм C++ предлагает исключение std:: Exception_list
, способное предоставить коллекцию std:: Exception_ptr
s. Пока это не станет общедоступным, давайте подделаем собственный список исключений
:
class exception_list : public std::runtime_error {
public:
exception_list( std::string const& what) :
std::runtime_error( what) {
}
typedef std::vector< std::exception_ptr > bundle_t;
typedef bundle_t::const_iterator iterator;
std::size_t size() const noexcept {
return bundle_.size();
}
iterator begin() const noexcept {
return bundle_.begin();
}
iterator end() const noexcept {
return bundle_.end();
}
void add( std::exception_ptr ep) {
bundle_.push_back( ep);
}
private:
bundle_t bundle_;
};
Теперь мы можем построить wait_first_success()
, используя wait_first_outcome_impl()
.
unbounded_channel::pop()
call
навсегда.
.unbounded_channel::pop()
call
would block forever.
[ORIG_END] -->
При наличии готового future<>
, можно отличить отказ, позвонив future::get_ Exception_ptr()
. Если будущее<>
на самом деле содержит результат, а не исключение, get_ Exception_ptr()
возвращает nullptr
. В этом случае мы можем уверенно позвонить будущее::get()
, чтобы вернуть результат нашему абоненту.
nullptr
, однако, мы собираем
exception_list
future<>
от канала.
.
Если мы выпадаем из цикла — если каждое отдельное волокно задачи бросает исключение — мы бросаем исключение Исключение_список
, в который мы собрали те std:: Исключение_ptr
.
template< typename Fn, typename ... Fns >
typename std::result_of< Fn() >::type
wait_first_success( Fn && function, Fns && ... functions) {
std::size_t count( 1 + sizeof ... ( functions) );
typedef typename std::result_of< typename std::decay< Fn >::type() >::type return_t;
typedef boost::fibers::future< return_t > future_t;
typedef boost::fibers::unbounded_channel< future_t > channel_t;
auto channelp( std::make_shared< channel_t >() );
wait_first_outcome_impl< return_t >( channelp,
std::forward< Fn >( function),
std::forward< Fns >( functions) ... );
exception_list exceptions("wait_first_success() produced only errors");
for ( std::size_t i = 0; i < count; ++i) {
future_t future( channelp->value_pop() );
std::exception_ptr error( future.get_exception_ptr() );
if ( ! error) {
channelp->close();
return future.get();
}
exceptions.add( error);
}
throw exceptions;
}
Звонок может выглядеть так:
std::string result = wait_first_success(
[](){ return sleeper("wfss_first", 50, true); },
[](){ return sleeper("wfss_second", 100); },
[](){ return sleeper("wfss_third", 150); });
std::cout << "wait_first_success(success) => " << result << std::endl;
assert(result == "wfss_second");