2017-05-09 3 views
1

Ich habe eine for-Schleife ein std::vector von std::future zu definieren meine Funktion vector<int> identify und eine weitere Schleife auszuführen, um die Ergebnisse zu erhalten, durch den Aufruf std::future::get() wie folgt:Beginnt ein Thread wirklich, nachdem std :: future :: get() aufgerufen wurde?

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]; 
} 

Der Job ist es, einige Bilder und ich teile die Arbeit zu verarbeiten ungefähr gleich zwischen jedem Faden. Ich bette auch std::cout in die Funktion ein, um anzuzeigen, aus welchem ​​Thread die Ergebnisse stammen.

Ich gehe davon aus, dass nach dem ersten Thread seine Arbeit beendet hat, andere auch ihre vervollständigt sollte und die Schleife wird die Ergebnisse aus drucken. Nach dem Ende des ersten Threads funktionieren jedoch noch andere Threads. Ich denke, dass sie wirklich funktionieren, nicht nur die Ergebnisse ausdrucken, weil es eine gewisse Verzögerung gibt, wenn die Funktion ein großes Bild verarbeitet. Das lässt mich fragen, wann die Fäden wirklich anfangen.

Ich weiß aus der Dokumentation, die jeder Thread nach seiner Initialisierung beginnt sofort, aber wie kann man meine Beobachtung erklären? Vielen Dank und jede Hilfe ist viel

+4

Bitte senden Sie eine [MCVE] – Jonas

+0

i hinzugefügt meinen Code-Snippet für Klarheit . –

+0

Mein erster Gedanke ist, dass Ihr Cout in einem Puffer kommen kann und nicht direkt gedruckt wird. Versuchen Sie, eine Zeit/Uhr auszudrucken, um sicher zu sein, wenn es startet, darüber hinaus bin ich nicht vertraut mit Threads. – turoni

Antwort

1

Da Sie std::launch::async verwenden, ist es bis zu std::async zu bestimmen, wie Sie Ihre Anforderungen planen. Nach cppreference.com:

Die Template-Funktion Asynchron läuft die Funktion f asynchron (möglicherweise in einem separaten Thread, den Teil eines Thread-Pools sein kann) und gibt ein std::future, das schließlich das Ergebnis dieses Funktionsaufruf halten wird.

Es garantiert, dass sie eingefädelt werden wird, aber, und Sie können bei der nächsten sich bietenden Gelegenheit geschehen wird die Auswertung Ihrer Lambda geplant folgern, dass:

Wenn die async Flagge gesetzt ist (dh policy & std::launch::async != 0), führt dann async das aufrufbare Objekt F auf einen neuen Ausführungs-Thread (mit allen gewinde einheimischen initialisiert), wie durch std::thread(std::forward<F>(f), std::forward<Args>(args)...) hervorgebracht, wenn, mit der Ausnahme, dass, wenn die Funktion f einen Wert oder löst eine Ausnahme gibt, ist es im freigegebenen Zustand gespeichert über die std::future zugänglich, die async zum Aufrufer zurückgibt.

Für Ihre Frage wollten Sie jedoch nur wissen, wann es in Bezug auf Ihren Anruf an get ausgeführt wird.Es ist leicht zu zeigen, dass get nichts mit der Ausführung von Asynchron-Aufgaben zu tun, wenn sie mit std::launch::async ins Leben gerufen:

#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; 
} 

Ausgang (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 

Diese Operationen sind trivial, aber wenn Sie lange haben Bei der Ausführung von Aufgaben können Sie erwarten, dass einige oder alle dieser Aufgaben möglicherweise noch ausgeführt werden, wenn Sie std::future<T>::get() aufrufen. In diesem Fall wird Ihr Thread ausgesetzt, bis die Versprechen mit der zukünftigen erfüllt ist. Da die asynchronen Tasks in einem Pool zusammengefasst werden können, ist es auch möglich, dass einige erst dann mit der Auswertung beginnen, wenn andere abgeschlossen sind.

Wenn Sie stattdessen std::launch::deferred verwenden, dann werden Sie faul Auswertung auf dem anrufenden Thread bekommen, und so würde die Ausgabe so etwas sein:

[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

Sie sollten 'cout' Zugriffe schützen. – Jarod42

+0

Vielen Dank für Ihre Antwort. Es löste jetzt meine Zweifel. Ich habe eine andere Frage. Im Code drucken Sie die verstrichene Zeit, aber wie kann ich die Zeit drucken, wenn die Funktion von einem Thread aufgerufen wird. Ich habe 'auto t = time (nullptr); auto tm = * localtime (&t); 'innerhalb der Funktion aber ich bin nicht sicher, ob es richtig ist. –

3

allgemein anerkannt, in Zukunft Muster get in der Regel blockiert, bis das Ergebnis in die Zukunft gesetzt. Dieses Ergebnis kann auch eine Ausnahme darstellen. Wenn Sie also ein Ergebnis aus dem anderen Thread setzen, wird die Sperre aufgehoben. Das Gleiche tritt auf, wenn die Ausnahme in die Zukunft propagiert wird.

Hier ist der Link zu cppreference.com, die diese beschreibt:

Die get-Methode wartet, bis die Zukunft ein gültiges Ergebnis hat und (je nachdem, welche Vorlage verwendet wird) ruft er. Es ruft effektiv wait() auf, um auf das Ergebnis zu warten.

Es kann nicht garantiert werden, dass Threads in der Reihenfolge ausgeführt werden, in der Sie sie erstellt haben, oder sogar in der Auftragsanwendung sth. dafür zu tun.

Es kann passieren, dass Sie Glück haben und auf die Zukunft warten, die das Ergebnis für den letzten Job in der Warteschlange enthält und somit alle anderen erledigt sind und Sie keine Blockierung bemerken, aber es kann auch umgekehrt sein.

Std :: Async: garantiert keine asynchrone Ausführung. Hier ist, was reference states:

Die Template-Funktion Asynchron läuft die Funktion f asynchron (möglicherweise in einem separaten Thread, den Teil eines Thread-Pools sein kann) und gibt eine std :: Zukunft, die schließlich wird das Ergebnis halten dieses Funktionsaufrufs.

Weitere heißt es:

Wenn die Asynchron-Flag gesetzt ist, dann async das aufrufbare Objekt f auf einem neuen Thread ausgeführt wird (dh Politik & std :: Start :: async = 0!) der Ausführung (mit allen thread-locals initialisiert) als ob sie von std :: thread (std :: forward (f), std :: forward (args) ...) erzeugt würde, außer dass die Funktion f einen Wert oder zurückgibt löst eine Ausnahme aus und wird im freigegebenen Status gespeichert, auf den über std :: future, das async zurückgibt, zugegriffen werden kann.

Können Sie zunächst versuchen, eine Version von std::async ohne Politik zu laufen, es sollte/könnte ein Innengewinde-Pool wiederverwendet werden. Wenn es schneller ausgeführt wird, liegt möglicherweise das Problem vor, dass die Anwendung Threads nicht erneut verwendet.

Schließlich hat die Bezugnahme auf async eine Notiz, die, wenn der Zustand der Ausführung synchron sein kann:

Die Implementierung des Verhaltens des ersten Überlast std :: erstrecken kann async durch zusätzliche Freigabe (implementierungsdefinierte) Bits in der Standard-Launch-Policy. Beispiele für die Implementierung definierte Einführung Politik sind die Sync-Politik (execute sofort, innerhalb des Asynchron-Aufrufs) und die Aufgabe Politik (ähnlich ASYNC, aber fad Einheimischen nicht gelöscht werden)

Wenn die std :: Zukunft erhalten aus std :: async von nicht bewegt wird, oder zu einer Referenz gebunden, blockiert der Destruktor der std :: Zukunft am Ende des vollständigen Ausdrucks, bis die asynchrone Operation, im wesentlichen Code machen wie die folgenden synchronen vervollständigt:

Debugging Multi-Thread-Anwendungen ist ein bisschen schwierig. Ich schlage vor, nur einen zusätzlichen Thread zu erstellen und alle Jobs/Futures durchlaufen zu lassen und zu sehen, ob es bei der Ausführung einige Missverständnisse gibt. An diesem Punkt würde der Haupt-Thread nicht stören, da er nur auf Ergebnisse wartet.

Sie können auch eine thread-sichere Logging-Bibliothek verwenden (zB Boost Log) und aufzeichnen, was dort abläuft und wie viele verschiedene Threads von std::async erstellt werden, indem Sie die Thread-ID abmelden und diese Threads erneut überhaupt benutzt.

+0

Vielen Dank für die Referenzen. Klar, ich habe viele Informationen verpasst. Danke für die Antwort noch einmal. –

+0

Sie sind willkommen :) – ovanes

Verwandte Themen