Самый простой случай, когда вам только нужно знать, что первая из множества асинхронных задач завершена — но вам не нужно получать возвратную стоимость, и вы уверены, что они не будут делать исключений.
Для этого мы вводим Done
класс, чтобы обернуть bool
переменную с кондиционирование_variable
и mutex
:
struct Done {
private:
boost::fibers::condition_variable cond;
boost::fibers::mutex mutex;
bool ready = false;
public:
typedef std::shared_ptr< Done > ptr;
void wait() {
std::unique_lock< boost::fibers::mutex > lock( mutex);
cond.wait( lock, [this](){ return ready; });
}
void notify() {
{
std::unique_lock< boost::fibers::mutex > lock( mutex);
ready = true;
}
cond.notify_one();
}
};
Модель, которой мы следим по всему этому разделу, состоит в том, чтобы передать std::shared_ptr<
соответствующему объекту синхронизации с функциями различных задач. Это устраняет отстающие вопросы о продолжительности жизни объекта синхронизации относительно последнего из волокон.
wait_first_simple()
использует эту тактику для Done
:
template< typename ... Fns >
void wait_first_simple( Fns && ... functions) {
auto done( std::make_shared< Done >() );
wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
done->wait();
}
wait_first_simple_impl()
- обычная рекурсия по пачке аргументов, захватывая Done::ptr
для каждого нового волокна:
void wait_first_simple_impl( Done::ptr) {
}
template< typename Fn, typename ... Fns >
void wait_first_simple_impl( Done::ptr done, Fn && function, Fns && ... functions) {
boost::fibers::fiber( [done, function](){
function();
done->notify();
}).detach();
wait_first_simple_impl( done, std::forward< Fns >( functions) ... );
}
Тело ягненка волокна чрезвычайно простое, как и обещалось: позвоните функции, сообщите Done
, когда она вернется. Первое волокно, чтобы сделать это, позволяет wait_first_simple()
возвратить — именно поэтому полезно иметь std::shared_ptr<Done>
управлять продолжительностью жизни нашего Done
вместо того, чтобы объявить его стекной переменной в wa><_.
Вот как вы можете назвать это:
wait_first_simple(
[](){ sleeper("wfs_long", 150); },
[](){ sleeper("wfs_medium", 100); },
[](){ sleeper("wfs_short", 50); });
В этом примере контроль возобновляется после wait_first_simple()
, когда sleeper("wfs_short", 50)
завершает — хотя остальные два sleeper()
волокна все еще работают.