Visual C++ utilise le pool de threads Windows (Vista CreateThreadpoolWork
, si disponibles, et QueueUserWorkItem
si non) lors de l'appel d' std::async
avec std::launch::async
.
Le nombre de threads dans la piscine est limitée. Si créer plusieurs tâches qui s'exécutent pendant une longue période sans sommeil (y compris I/O), les tâches à venir dans la file d'attente n'obtiendrez pas une chance de travailler.
La norme (je suis en utilisant N4140) dit que l'utilisation d' std::async
avec std::launch::async
... des appels
INVOKE(DECAY_COPY(std::forward<F>(f)), DECAY_COPY(std::forward<Args>(args))...)
(20.9.2, 30.3.1.2) comme si dans un nouveau thread d'exécution représentée par un objet thread avec les appels à l'DECAY_COPY()
cours d'évaluation dans le thread qui appelleasync
.
(§30.6.8p3, c'est moi qui souligne.)
std::thread
s'constructeur crée un nouveau thread, etc.
À propos des threads en général, il est dit (§1.10p3):
Implémentations devraient veiller à ce que tous débloqué threads, éventuellement, de faire des progrès. [Remarque: les fonctions de bibliothèque Standard peuvent bloquer silencieusement sur les I/O ou des serrures. Les facteurs dans l'environnement d'exécution, y compris imposés de l'extérieur les priorités des threads, ce qui peut empêcher une mise en œuvre de la fabrication de certaines garanties de la progression. -la note de fin]
Si je crée un tas d'OS de threads ou de l' std::thread
s, tout en effectuant quelques très longtemps (peut-être infini) des tâches, ils vont tous être prévue (au moins sur Windows; sans vous embêter avec des priorités, des affinités, etc.). Si nous programmons les mêmes tâches pour le pool de threads Windows (ou utilisez std::async(std::launch::async, ...)
qui le fait), plus tard les tâches planifiées ne fonctionnera pas jusqu'à ce que le plus tôt les tâches de finition.
Est-ce légal, à proprement parler? Et ce n'est "finalement" signifie?
Le problème est que si les tâches planifiées premiers sont de facto à l'infini, le reste des tâches ne fonctionne pas. Donc, les autres threads (pas OS fils, mais le "C++-fils", selon le comme-si la règle) ne ferez pas de progrès.
On peut arguer que si le code a une boucle infini le comportement est indéfini, et donc c'est légal.
Mais je soutiens que nous n'avons pas besoin d'une boucle infinie de la problématique genre que dit la norme causes UB pour que cela se produise. L'accès volatils objets, l'exécution d'opération atomique et les opérations de synchronisation sont tous les effets secondaires que l'option "désactiver" l'hypothèse sur les boucles de mettre fin.
(J'ai un tas d'appels asynchrones de l'exécution de la suite de lambda
auto lambda = [&] {
while (m.try_lock() == false) {
for (size_t i = 0; i < (2 << 24); i++) {
vi++;
}
vi = 0;
}
};
et le verrou est relâché lors de la saisie de l'utilisateur. Mais il existe d'autres types de légitime les boucles infinies.)
Si je programme un couple de ces tâches, les tâches de l'annexe I après eux de ne pas arriver à courir.
Un vraiment méchants exemple serait le lancement de trop nombreuses tâches qui s'exécutent jusqu'à ce qu'une serrure de presse/un drapeau est hissé, puis planifier à l'aide de `std::async(std::lancement::asynchrone, ...) une tâche qui soulève le drapeau. À moins que le mot "éventuellement" signifie quelque chose de très surprenant, ce programme a pour résilier. Mais sous VC++ mise en œuvre, il ne le fera pas!
Pour moi, il semble comme une violation de la norme. Ce qui me fait me demander agit de la deuxième phrase de la note. Facteurs peuvent empêcher les implémentations de donner certaines garanties de la progression. Alors, comment sont ces mise en œuvre conforme?
C'est comme dire il peut y avoir des facteurs qui empêchent les implémentations de fournir certains aspect de la mémoire de la commande, l'atomicité, ou même de l'existence de plusieurs threads d'exécution. Grande, mais conformes hébergé implémentations doivent supporter plusieurs threads. Tant pis pour eux et leurs facteurs. Si elles ne peuvent pas eux c'est pas du C++.
Est-ce un assouplissement de l'exigence? Si l'interprétation de la sorte, c'est un retrait complet de l'exigence, car il ne précise pas quels sont les facteurs et, plus important encore, ce qui garantit peut-être pas fourni par les implémentations.
Si ce n'est pas ce que remarque même dire?
Je me souviens de notes de bas de page non-normatifs selon les Directives ISO/CEI, mais je ne suis pas sûr à propos de notes. J'ai trouvé dans les directives ISO/CEI suivantes:
24 Notes
24.1 But ou la raison d'être
Les Notes sont utilisés pour donner des informations supplémentaires destinées à faciliter la compréhension ou de l'utilisation du texte du document. Le document doit être utilisable sans les notes.
C'est moi qui souligne. Si je considère que le document sans que sait pas remarque, me semble threads doivent faire des progrès, std::async(std::launch::async, ...)
a pour effet -comme si le foncteur est d'exécuter sur un nouveau fil de discussion, comme si elle avait été créée à l'aide de std::thread
, et donc un foncteurs distribué à l'aide de std::async(std::launch::async, ...)
doit faire des progrès. Et dans le VC++ mise en œuvre avec le pool de threads ils ne le font pas. Donc, VC++ est en violation de la norme à cet égard.
Exemple complet, testé à l'aide de VS 2015U3 sur Windows 10 Enterprise 1607 sur i5-6440HQ:
#include <iostream>
#include <future>
#include <atomic>
int main() {
volatile int vi{};
std::mutex m{};
m.lock();
auto lambda = [&] {
while (m.try_lock() == false) {
for (size_t i = 0; i < (2 << 10); i++) {
vi++;
}
vi = 0;
}
m.unlock();
};
std::vector<decltype(std::async(std::launch::async, lambda))> v;
int threadCount{};
std::cin >> threadCount;
for (int i = 0; i < threadCount; i++) {
v.emplace_back(std::move(std::async(std::launch::async, lambda)));
}
auto release = std::async(std::launch::async, [&] {
__asm int 3;
std::cout << "foo" << std::endl;
vi = 123;
m.unlock();
});
return 0;
}
Avec 4 ou moins, il se termine. Avec plus de 4, il ne le fait pas.
Des questions similaires:
Est-il une implémentation de std::async qui utilise le pool de thread? - Mais il n'est pas question à propos de la légalité, et de ne pas avoir de réponse de toute façon.
std::async - mise en Œuvre dépend de l'utilisation? - Mentionne que les "pools de threads ne sont pas vraiment pris en charge", mais se concentre sur l'
thread_local
variables (qui est résoluble, même si le "pas simple" ou " non-trivial que la réponse et le commentaire de le dire) et ne tient pas compte de la note près de l'obligation de faire des progrès.