2017-05-09 6 views
1

私は次のようにstd::future::get()を呼び出すことによって結果を得るために、私の機能vector<int> identifyと別のループを実行するためにstd::futurestd::vectorを定義するためのforループを作成しました:std :: future :: get()が呼び出された後にスレッドが実際に起動しますか?

for (int i = 0; i < NUM_THREADS; ++i) { 
    VecString::const_iterator first = dirList.begin() + i*share; 
    VecString::const_iterator last = i == NUM_THREADS - 1 ? dirList.end() : dirList.begin() + (i + 1)*share; 
    VecString job(first, last); 
    futures[i] = async(launch::async, [=]() -> VecInt { 
     return identify(i, job, make_tuple(bIDList, wIDList, pIDList, descriptor), testingDir, binaryMode, logFile, logFile2); 
    }); 
} 

int correct = 0; 
int numImages = 0; 
for(int i = 0; i != NUM_THREADS; ++i) { 
    VecInt ret = futures[i].get(); 
    correct += ret[0]; 
    numImages += ret[1]; 
} 

ジョブは、いくつかの画像を処理することであり、私は仕事を分割各スレッド間でほぼ等しくなります。私はまた結果に由来するスレッドを示すためにstd::coutを関数に埋め込みます。

最初のスレッドが処理を終えた後、他のスレッドも処理を完了し、ループが結果を出力することが期待されます。しかし、最初のスレッドが終了した後も、他の​​スレッドは引き続き動作します。関数が大きなイメージを処理するときに遅延があるため、結果を出力するだけでなく、実際に作業すると思います。これはスレッドが本当にいつ始まるのか不思議です。

私は、各スレッドが初期化後すぐに起動することをドキュメントから知っていますが、どのように私の観察を説明することができますか?大変ありがとうございます。どんな助けでも大変ありがとうございます。

+4

[MCVE] – Jonas

+0

を投稿してください私は、明確にするために私のコードスニペットを追加しました。 –

+0

私の最初の考えは、あなたのcoutがバッファーに入ってきて、直接印刷されないということです。 起動時に確実にスレッドを使い慣れていない時間/クロックを印刷してみてください。 – turoni

答えて

1

std::launch::asyncを使用しているため、要求をスケジュールする方法を決定するのは最大でstd::asyncです。 cppreference.comによれば:

テンプレート関数非同期は、(潜在的に、スレッドプールの一部であってもよく、別のスレッドで)非同期関数fを実行し、最終的にその関数呼び出しの結果を保持するstd::futureを返します。

それは、彼らはしかし、スレッド化され、あなたがラムダの評価は次の使用可能な機会に起こるようにスケジュールされることが推測できることを保証しない:

非同期フラグ場合が設定されている場合(つまりpolicy & std::launch::async != 0)、asyncは、新しいスレッドの実行時に呼び出し可能オブジェクトfを実行します(すべてのスレッドローカルが初期化された状態で)。std::thread(std::forward<F>(f), std::forward<Args>(args)...)で生成されたものと同じです。ただし、関数fが値を返すか、 std::futureからアクセス可能な共有状態で保存され、asyncが呼び出し元に戻ります。あなたの質問の目的のために

、しかし、あなたはそれがgetへの通話に関連して実行されます時に知りたいと思いました。それはstd::launch::asyncで起動するときgetは、非同期タスクの実行とは何の関係もないことを証明するのは簡単です:

#include <iostream> 
#include <future> 
#include <thread> 
#include <vector> 
#include <chrono> 

using namespace std; 

int main() { 
    auto start = chrono::steady_clock::now(); 
    auto timestamp = [start](ostream & s)->ostream& { 
     auto now = chrono::steady_clock::now(); 
     auto elapsed = chrono::duration_cast<chrono::microseconds>(now - start); 
     return s << "[" << elapsed.count() << "us] "; 
    }; 

    vector<future<int>> futures; 
    for(int i = 0; i < 5; i++) 
    { 
     futures.emplace_back(async(launch::async, 
      [=](){ 
       timestamp(cout) << "Launch " << i << endl; 
       return i; 
      })); 
    } 

    this_thread::sleep_for(chrono::milliseconds(100)); 

    for(auto & f : futures) timestamp(cout) << "Get " << f.get() << endl; 

    return 0; 
} 

出力(live example here):あなたは長い間持っている場合

[42us] Launch 4 
[85us] Launch 3 
[95us] Launch 2 
[103us] Launch 1 
[109us] Launch 0 
[100134us] Get 0 
[100158us] Get 1 
[100162us] Get 2 
[100165us] Get 3 
[100168us] Get 4 

これらの操作は些細ですが、実行中のタスクの場合、std::future<T>::get()に電話すると、それらのタスクの一部またはすべてがまだ実行されている可能性があります。その場合、あなたのスレッドは約束と関連付けられ、それはの将来のが満たされるまで延期されます。また、非同期タスクがプールされる可能性があるため、他のタスクが完了するまで評価を開始しない可能性もあります。

あなたの代わりにstd::launch::deferredを使用する場合は、あなたが呼び出し元のスレッドに遅延評価を得るでしょうし、その出力はのようになります。

[100175us] Launch 0 
[100323us] Get 0 
[100340us] Launch 1 
[100352us] Get 1 
[100364us] Launch 2 
[100375us] Get 2 
[100386us] Launch 3 
[100397us] Get 3 
[100408us] Launch 4 
[100419us] Get 4 
[100430us] Launch 5 
+0

あなたは 'cout'アクセスを保護する必要があります。 – Jarod42

+0

ありがとうございます。それは今、私の疑いを明らかにした。私は別の質問があります。コードでは経過時間を表示しますが、関数がスレッドによって呼び出された時刻をどのように表示することができます。私は 'auto t = time(nullptr);を使いました。 auto tm = * localtime(&t); '関数内ですが、正しいかどうかわかりません。 –

3

通常、将来のパターンgetは通常、結果が未来になるまでブロックします。この結果は例外でもあります。したがって、他のスレッドから結果を設定すると、getはブロック解除されます。例外が将来に伝播される場合も同様です。ここ

これを説明cppreference.comへのリンクである:

getメソッドは、将来の有効な結果を有するまで待機し(どのテンプレートに応じて使用されている)、それを検索します。結果を待つためにwait()を効果的に呼び出します。

スレッドは、作成した順序で実行することは保証されません。また、アプリケーションが割り当てられた順序で実行することも保証されません。それがするために。

最終的にキューに入れられたジョブの結果が残っているため、将来他のすべてのタスクが完了し、ブロックされていることに気付かないこともありますが、逆もあります。

std :: async:非同期実行を保証しません。ここでは何reference statesです:

テンプレート関数の非同期は、(スレッドプールの一部であってもよく、別のスレッドに潜在的に)非同期関数fを実行し、最終的に結果を保持するのstd ::未来を返しますその関数呼び出しの。

がさらに記載されている:非同期フラグが設定されている

場合は、非同期は新しいスレッドで呼び出し可能オブジェクトfを実行する(すなわち政策&のstd ::打ち上げ::非同期= 0!) std :: thread(std :: forward(f)、std :: forward(args)...)によって生成されたかのように実行(すべてのスレッドローカルが初期化されている)例外をスローすると、std :: futureを通じてアクセス可能な共有状態に格納され、asyncが呼び出し元に戻ります。

あなたが最初にそれが/内部スレッドプールを再利用する可能性がある必要があり、いずれの政策なしstd::asyncのバージョンを実行しようとすることができます。それが速く実行されている場合、アプリケーションがスレッドを再利用しないことが問題である可能性がありますか?追加の可能な実装は、STDの最初の過負荷の動作を拡張することができる

::によって非同期(async)(:

最後に、非同期への言及は、実行同期することができる状態のノートを有しますインプリメンテーション定義の)ビットを使用します。実装定義の起動ポリシーの 例同期ポリシー(非同期呼び出しの中に、すぐに実行)、タスク方針です(非同期に似てますが、スレッド地元の人々がクリアされません)

のstd ::未来が得られた場合std :: asyncが参照から移動されたり参照にバインドされたりしていない場合、std :: futureのデストラクタは非同期操作が完了するまで完全な式の最後にブロックされ、基本的に次のようなコードが生成されます

std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f() 
std::async(std::launch::async, []{ g(); }); // does not start until f() completes 

マルチスレッドアプリケーションのデバッグは少し難しいです。私はちょうど1つの追加のスレッドを作成し、すべての仕事/先物を実行し、実行にいくつかの誤解があるかどうかを確認することをお勧めします。この時点では、メインスレッドは結果を待つだけで邪魔にならないでしょう。

また、スレッドセーフ(ブーストログなど)のログライブラリを使用して、何が起こっているのか、そしてstd::asyncによっていくつの異なるスレッドが作成されたのかを記録することができます。まったく使用されません。

+0

ありがとうございます。明らかに、私は多くの情報を見逃していました。答えをもう一度ありがとう。 –

+0

あなたは大歓迎です:) – ovanes

関連する問題