8

「先物は正しく行われた」という記事を読んだばかりですが、c ++ 11が約束していない主なことは、既存の先物から複合先物を作成することです。

私は今、boost::wait_for_anyのドキュメントを見ています

ただし、次の例を検討してください。

int calculate_the_answer_to_life_the_universe_and_everything()
{
    return 42;
}

int calculate_the_answer_to_death_and_anything_in_between()
{
    return 121;
}

boost::packaged_task<int> pt(calculate_the_answer_to_life_the_universe_and_everything);
boost:: future<int> fi=pt.get_future();
boost::packaged_task<int> pt2(calculate_the_answer_to_death_and_anything_in_between);
boost:: future<int> fi2=pt2.get_future();

....


int calculate_the_oscillation_of_barzoom(boost::future<int>& a, boost::future<int>& b)
{
    boost::wait_for_all(a,b);
    return a.get() + b.get();
}

boost::packaged_task<int> pt_composite(boost::bind(calculate_the_oscillation_of_barzoom, fi , fi2));
boost:: future<int> fi_composite=pt_composite.get_future();

構成可能性へのこのアプローチの何が問題になっていますか?これは、構成可能性を実現するための有効な方法ですか?このパターンにエレガントな構文の教育者が必要ですか?

4

2 に答える 2

6

when_any先物をwhen_all構成するための完全に有効な方法です。これらは両方とも並列合成に対応し、複合操作は1つまたはすべての合成操作を待機します。

シーケンシャルコンポジションも必要です(Boost.Threadにはありません)。これは、たとえば、future<T>::thenfutureの値を使用し、futureの準備ができたときに実行される操作をキューに入れることができる関数である可能性があります。これを自分で実装することは可能ですが、効率のトレードオフがあります。ハーブサッターは彼の最近のChannel9ビデオでこれについて話します。

N3428は、これらの機能(およびその他)をC++標準ライブラリに追加するためのドラフト提案です。これらはすべてライブラリ機能であり、言語に新しい構文を追加することはありません。さらに、N3328は、内部で使用する再開可能な関数(C#でのasync/の使用など)の構文を追加するための提案です。awaitfuture<T>::then

于 2013-01-05T18:00:41.040 に答える
3

edulcorantという単語の使用のポイント。:)

サンプルコードの問題は、すべてをタスクにパッケージ化することですが、それらのタスクの実行をスケジュールすることはありません。

int calculate_the_answer_to_life() { ... }

int calculate_the_answer_to_death() { ... }

std::packaged_task<int()> pt(calculate_the_answer_to_life);
std::future<int> fi = pt.get_future();
std::packaged_task<int()> pt2(calculate_the_answer_to_death);
std::future<int> fi2 = pt2.get_future();

int calculate_barzoom(std::future<int>& a, std::future<int>& b)
{
    boost::wait_for_all(a, b);
    return a.get() + b.get();
}

std::packaged_task<int()> pt_composite([]{ return calculate_barzoom(fi, fi2); });
std::future<int> fi_composite = pt_composite.get_future();

この時点で私が書くなら

pt_composite();
int result = fi_composite.get();

私のプログラムは永久にブロックされます。pt_compositeでブロックされcalculate_barzoom、でブロックされwait_for_all、でブロックされ、両方でブロックさfiれるため、完了しか、それぞれfi2実行するまで完了しませんそして、私のプログラムがブロックされているので、誰もそれらを実行することはありません!ptpt2

あなたはおそらく私がこのようなものを書くことを意味しました:

std::async(pt);
std::async(pt2);
std::async(pt_composite);
int result = fi_composite.get();

これは機能します。ただし、これは非常に非効率的asyncです。2つのスレッドに相当する作業を実行するために、(への3つの呼び出しを介して)3つのワーカースレッドを生成します。その3番目のスレッド(実行中のスレッド)pt_compositeはすぐに生成され、実行が終了するまでスリープpt状態になります。pt2これは回転するよりはましですが、存在しないよりもかなり悪いです。つまり、スレッドプールのワーカーが本来あるべき数よりも1つ少ないということです。CPUコアごとに1つのスレッドしかなく、常に多くのタスクが発生するもっともらしいスレッドプールの実装では、これは、1つのCPUコアがアイドル状態になっていることを意味します。そのコアで実行されることは、現在、内部でブロックされていwait_for_allます。

私たちがやりたいのは、私たちの意図を宣言的に宣言することです。

int calculate_the_answer_to_life() { ... }

int calculate_the_answer_to_death() { ... }

std::future<int> fi = std::async(calculate_the_answer_to_life);
std::future<int> fi2 = std::async(calculate_the_answer_to_death);
std::future<int> fi_composite = std::when_all(fi, fi2).then([](auto a, auto b) {
    assert(a.is_ready() && b.is_ready());
    return a.get() + b.get();
});

int result = fi_composite.get();

次に、ライブラリとスケジューラを連携させて、正しいことを実行します。タスクをすぐに続行できないワーカースレッドを生成しないでください。エンドユーザーが明示的にスリープ、待機、またはブロックするコードを1行でも記述しなければならない場合、パフォーマンスの一部が確実に失われます。

言い換えると、その時間の前にワーカースレッドを生成しません。


明らかに、ライブラリのサポートなしで、これらすべてを標準のC++で実行することは可能です。これがライブラリ自体の実装方法です。しかし、最初から実装するのは非常に苦痛であり、多くの微妙な落とし穴があります。そのため、ライブラリのサポートが間もなく開始されるのは良いことです。

Roshan Shariffの回答で言及されているISO提案N3428はN3857として更新され、N3865はさらに便利な機能を提供します。

于 2015-09-08T06:10:27.937 に答える