Task based concurrency



  • Hallo,

    ich befolge grade die Tutorial Serie von Bartosz Milewski zum Thema Concurrency und habe eine Frage zum Task based Ansatz mit Async. Der vollständigkeit halber hier einmal der Link: https://www.youtube.com/watch?v=_Ll0PIobErE, 15te minute.

    Mein Quellcode sieht genauso aus, wie bei ihm, wenn ich mein Programm aber ausführe, werden alle Tasks in dem main Thread ausgeführt. Bei ihm werden die ersten 6 Tasks in unterschiedlichen threads ausgeführt und nur die übrigen 4 anschließend im main thread.

    Ich habe das Programm auf einer Maschine mit 4 Cores ausgeführt und alle tasks wurden sequenziell auf dem main thread abgearbeitet. Daher wollte ich mal nachfragen, woran das liegt und ob ich doch etwas falsch gemacht habe.

    Hier ist der Code von mir und dem Tutorial:

    int main() {
    
    	std::cout << "main thread: " << std::this_thread::get_id() << std::endl;
    
    	std::vector<std::future<void>> futures;
    	for (int i = 0; i != 10; ++i) {
    		auto fut = std::async([] {
    			std::this_thread::sleep_for(std::chrono::seconds(2));
    			std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
    		});
    
    		futures.push_back(std::move(fut));
    	}
    
    	std::for_each(futures.begin(), futures.end(), [](std::future<void> &f){
    		f.wait();
    	});
    
    	std::cout << std::endl;
    	std::this_thread::sleep_for(std::chrono::seconds(10));
    
    	return 0;
    }
    

  • Mod

    std::async hat auch einen Overload der die Launch Policy angibt. Du hast den ohne explizite Policy Angabe gewählt:

    Behaves the same as async(std::launch::async | std::launch::deferred, f, args...) . In other words, f may be executed in another thread or it may be run synchronously when the resulting std::future is queried for a value.

    Du möchtest wohl nur std::launch::async angeben, damit tatsächlich ein neuer Thread zur Ausführung verwendet wird.

    auto fut = std::async(std::launch::async, [] {
                std::this_thread::sleep_for(std::chrono::seconds(2));
                std::cout << "thread id: " << std::this_thread::get_id() << std::endl;
            });
    


  • Hallo Arcoth,

    vielen Dank für deine Antwort. Ich hab noch eine weitere Frage dazu: Wovon hängt es denn ab, ob ein Task in einem separatem Thread ausgeführt wird oder im main thread, wenn ich keine run policy übergebe?



  • Async schrieb:

    Hallo Arcoth,

    vielen Dank für deine Antwort. Ich hab noch eine weitere Frage dazu: Wovon hängt es denn ab, ob ein Task in einem separatem Thread ausgeführt wird oder im main thread, wenn ich keine run policy übergebe?

    Hängt laut CPP-Reference von der Implementationm der STL ab.

    If both the std::launch::async and std::launch::deferred flags are set in policy, it is up to the implementation whether to perform asynchronous execution or lazy evaluation.



  • Der Sinn davon ist, dass die Implementierung selber entscheiden können soll, was in einer Situation effizienter ist. Kann der Compiler beispielsweise erkennen, dass die Funktion kaum Zeit benötigen wird, kann er sie bei deferred direkt zum get verschieben.

    Das kann er auch machen, wenn ein Thread auf N Futures wartet: Er erzeugt (N-1) zusätzliche Threads und erledigt die übrig gebliebene Aufgabe im Thread, der ansonsten nur warten würde.

    In generischem Code ist das sinnvoll, wenn die Art der Arbeit noch nicht abzusehen ist. Ob das ein Compiler bzw eine Standardbibliothek so kompliziert umsetzt, ist eine andere Frage.

    async ist wie eine Annotation, die dem Compiler eine Zusatzinformation liefert. Wenn if eine räumliche Verzweigung ist, ist async eine zeitliche: "Dieser Funktionsaufruf ist zeitlich unabhängig von den Statements danach". Was sich verzweigt, muss irgendwann wieder zusammengeführt werden. future modelliert die nicht abgeschlossene Verzweigung, die man mit get abschließen kann.


Anmelden zum Antworten