2017-11-30 2 views
3

Wie ich es verstehe, planen die üblichen Implementierungen von std :: async diese Jobs auf Threads aus einem zuvor zugewiesenen Thread-Pool.Können lang-laufende std :: asyncs andere std :: asyncs verhungern?

Also lassen Sie mich sagen, ich zuerst erstellen und planen Sie lange genug std::async s, um alle Threads aus diesem Thread-Pool besetzt zu halten. Direkt danach (kurz bevor sie fertig sind) erstelle ich auch einige kurzlaufende std::async s. Kann es passieren, dass die Kurzläufer überhaupt nicht ausgeführt werden, bis mindestens einer der Langläufer beendet ist? Oder gibt es eine Garantie im Standard (speziell C++ 11), die diese Art von Situation verhindert (wie das Erzeugen von mehr Threads, so dass das OS sie in einem Round-Robin-Modus planen kann)?

+1

Es ist abhängig von der Implementierung. Fragen Sie nach dem C++ - Standard oder einer bestimmten Implementierung? –

+1

Idealerweise hätte ich gerne eine Garantie vom Standard. Aber da Sie zu sagen scheinen, dass es keine solche Garantie gibt, bin ich speziell an dem Verhalten von GCC's libstdC++ interessiert (GCC 5.4, wenn das wichtig ist). – Dreamer

Antwort

2

Der Standard lautet:

[futures.async#3.1] Wenn Start :: async in Richtlinie festgelegt ist, ruft INVOKE(DECAY_­COPY(std​::​forward<F>(f)), DECAY_­COPY(std​::​forward<Args>(args))...) ([func.require], [thread.thread.constr]) als ob in ein neuer Thread der Ausführung, dargestellt durch ein Thread-Objekt, wobei die Aufrufe von DECAY_COPY in dem Thread ausgewertet werden, der async aufruft. [...]

so unter der as-if Regel neue Fäden hervorgebracht werden müssen, wenn async() mit ​async Starts Politik aufgerufen wird. Natürlich kann eine Implementierung intern einen Thread-Pool verwenden, aber abgesehen von dem üblichen Thread-Erzeugungsaufwand kann kein spezielles "Verhungern" auftreten. Außerdem sollten Dinge wie die Initialisierung von Thread-Locals immer passieren.

In der Tat, Klirren libC++ Stamm Asynchron-Implementierung lautet:

unique_ptr<__async_assoc_state<_Rp, _Fp>, __release_shared_count> 
     __h(new __async_assoc_state<_Rp, _Fp>(_VSTD::forward<_Fp>(__f))); 

VSTD::thread(&__async_assoc_state<_Rp, _Fp>::__execute, __h.get()).detach(); 

return future<_Rp>(__h.get()); 

wie Sie sehen können, keine 'explizite' Thread-Pool wird intern verwendet.

Außerdem, wie Sie lesen können here auch die libstdC++ Implementierung Versand mit gcc 5.4.0 ruft nur einen einfachen Thread.

+1

Obwohl, wenn die Richtlinie 'launch :: async | ist launch :: deferred '(der Standard) die Implementierung kann wählen zwischen – Caleth

+0

@Caleth, yep, ich nahm die Frage nur mit launch :: async Calls an (sonst würde die Frage wenig Sinn machen). BTW, gibt es auch Implementierung bereitgestellt Launch-Richtlinien sowie ... –

0

Ja, MSVCs std::async scheinen genau diese Eigenschaft zu haben, zumindest ab MSVC2015.

Ich weiß nicht, ob sie es in einem Update 2017 behoben haben.

Dies ist gegen den Geist des Standards. Der Standard ist jedoch äußerst vage bezüglich Thread-Forward-Fortschrittsgarantien (zumindest ab C++ 14). Während also std::async sich so verhalten muss, als ob es einen std::thread umschließt, sind die Garantien auf std::thread Vorwärtsfortschritt ausreichend schwach, dass dies unter der Als-ob-Regel keine große Garantie darstellt.

In der Praxis hat dies führt mich std::async in meinen Thread-Pool-Implementierungen mit rohen Anrufen std::thread, wie roh Verwendung von std::thread in MSVC2015 zu ersetzen scheint nicht das Problem zu haben.

Ich finde, dass ein Thread-Pool (mit einer Task-Queue) zu viel praktischer ist als rohe Anrufe entweder std::async oder std::thread, und wie es wirklich einfach ist, einen Thread-Pool zu schreiben entweder mit std::thread oder std::async, würde ich raten Schreiben eines mit std::thread.

Ihr Thread-Pool kann std::future s wie std::async zurückgeben (aber ohne die automatische Blockierung bei der Destruktion, da der Pool selbst die Thread-Lebensdauern verwaltet).

Ich habe gelesen, dass C++ 17 bessere Fortschritte Fortschritt Garantien hinzugefügt, aber ich habe nicht genügend Verständnis zu schließen, wenn MSVC Verhalten ist jetzt gegen die Standardanforderungen.

+0

* "die Garantien auf Std :: Thread Forward Fortschritt sind ausreichend schwach, dass dies nicht viel von einer Garantie unter der als-ob-Regel ist" * Ich bin nicht Sicher, das ist überzeugend; Der Buchstabe des Standards besagt, dass der beobachtbare Effekt von async der eines neuen Ausführungsstrangs sein sollte; Umgekehrt kann ein Programm, das unter der MSVC-Implementierung läuft, beobachtbare Hinweise sammeln, dass async() - Aufrufe sich anders verhalten als äquivalente Thread-Aufrufe (dies geschieht * unabhängig * von Thread-Ausführungsfortschrittsgarantien). –

+0

@MassimilianoJanes Ausgenommen der Standard * garantiert nicht, dass sich 'std :: thread 'vernünftig verhält *. Weder verhält sich anders als der Standard garantiert, dass sich ein 'std :: thread 'verhält. Lediglich das Verhalten außerhalb des abstrakten Maschinenverhaltens unterscheidet sich. Beobachtbares Verhalten muss im Sinne von "in Bezug auf die abstrakte Maschine und das von dieser Norm vorgeschriebene Verhalten" sein; andere Dinge (wie wenn Sie eine Stack-Spur machen) können natürlich nicht identisch sein. Zufälligerweise bedeutet unterspezifizierte Forward Progress Guarantees, dass "std :: thread" genau so schlecht sein könnte wie "async" ist ... – Yakk

+0

ja, und "thread (foo) .detach(); bar();" könnte * Benehmen Sie sich als "thread (foo) .join(); bar() unter der als-ob-Regel, können wir jedoch daraus schließen, dass diese beiden Codes * äquivalent sind * wie bei der as-if-Regel? –

Verwandte Themen