ハーブサッターの話 の関数then()
の実装についていくつか質問があります。この関数は非同期操作を連鎖させるために使用され、パラメーターf
は1つの操作からの未来であり、パラメーターw
はこの操作(ラムダ)の「作業」です。
_template <typename Fut, typename Work>
auto then(Fut f, Work w) -> future<decltype(w(f.get()))>
{
return async([=]{ w(f.get()); });
}
_
アプリケーションの例は次のとおりです。
_ std::future<int> f = std::async([]{
std::this_thread::sleep_for(std::chrono::microseconds(200));
return 10;
});
auto f2 = then(std::move(f), [](int i){
return 2 * i;
});
_
メインスレッドはタスクを生成しますが、タスクが終了するのを待ちません。
まず、_future<T>
_にはコピーコンストラクターがありません。つまり、提案された実装は、async()
の呼び出しを変更して未来をラムダに移動しない限り、_shared_future<T>
_でのみ使用できます。 これSO質問 それを行う方法を提案しましたが、複雑すぎるようです。関数を再実装しましたが、コードが正しいかどうか、または見逃したかどうか疑問に思っています何か...
次に、then()
関数に渡される未来はvoid
である可能性があるため、実際にはthen()
の2つの実装が必要です。 1つはT
を返す先物用で、もう1つはvoid
を返す先物用です。
最後に、then()
の本体内のラムダにreturnステートメントを含めて、実際に値を返すことができるようにする必要がありますか? returnステートメントがないと、_future<void>
_を返しますよね?
私は上記の点に対処しようとしました、そしてこれは私が思いついたものです。それが正しいか?
_template <typename T, typename Work>
auto then(future<T> f, Work w) -> future<decltype(w(f.get()))>
{
return async([](future<T> f, Work w)
{ return w(f.get()); }, move(f), move(w));
}
template <typename Work>
auto then(future<void> f, Work w) -> future<decltype(w())>
{
return async([](future<void> f, Work w)
{ f.wait(); return w(); }, move(f), move(w));
}
_
インターフェイスを簡素化するために、Herbがconcurrent<T>
実装で行ったのと同様に、実装内でvoid
問題を「非表示」にします。 2つのthen
実装を持つ代わりに、2つの実装でヘルパー関数get_work_done
を宣言します。
template <typename T, typename Work>
auto get_work_done(future<T> &f, Work &w)-> decltype(w(f.get()))
{return w(f.get());}
template <typename Work>
auto get_work_done(future<void> &f, Work &w)-> decltype(w())
{f.wait(); return w();}
そして、テンプレートパラメータの検出で残りを処理します。
template <typename T, typename Work>
auto then(future<T> f, Work w) -> future<decltype(w(f.get()))>
{
return async([](future<T> f, Work w)
{ return get_work_done(f,w); }, move(f), move(w));
}
.then()へのこのアプローチの問題は、2つのスレッド(コストがかかる)を同時に生成し、2番目のスレッドがfuture.get/waitでブロックすることです(もちろん、最初のスレッドが十分に長く実行される場合)。 、ワークキューを使用して、ジョブの実行順序をシリアル化する(および既存のスレッドをリサイクルする)ことをお勧めします。良いスレッドプールパターンの実装を探すだけです
いいえ、正しくありません。 .get()の戻り値を継続に渡すと、.get()から伝播された例外を処理できなくなります。 boost.threadの場合と同様に、futureをcontinueに渡し、.get()を手動で呼び出す必要があります。