web-dev-qa-db-fra.com

C ++ 11: std :: thread mis en commun?

En C++ 03, j'ai utilisé pthread avec un pool de threads auto-construit qui gardait toujours un couple de threads en cours d'exécution (depuis pthread_create est lent), j'ai ainsi pu lancer des threads pour de petites tâches sans penser aux problèmes de performances.

Maintenant, en C++ 11, nous avons std::thread. Je suppose que la norme ne dit rien sur l'implémentation spécifique, donc ma question concerne les implémentations de bibliothèque standard. Opte-t-on généralement pour une approche mutualisée où la construction de std::threads est bon marché (et, par exemple, n'appelle pas pthread_create sur posix), ou std::thread juste être un wrapper?

En d'autres termes, un pool de threads est-il toujours recommandé en C++ 11, ou dois-je simplement créer un std::thread chaque fois que j'en ai besoin et que je laisse les performances à la bibliothèque standard?

38
lucas clemente

En général, std::thread Doit être un encapsulage minimal autour de la primitive système sous-jacente. Par exemple, si vous êtes sur la plate-forme pthread, vous pouvez tester avec le programme suivant que peu importe le nombre de threads que vous créez, ils sont tous créés avec des identifiants pthread_t Uniques (ce qui implique qu'ils ' recréé à la volée et non emprunté à un pool de threads):

#include <assert.h>
#include <mutex>
#include <set>
#include <thread>
#include <vector>

#include <pthread.h>

int main() {
  std::vector<std::thread> workers;
  std::set<long long> thread_ids;
  std::mutex m;
  const int n = 1024;

  for (int i = 0; i < n; ++i) {
    workers.Push_back(std::thread([&] {
      std::lock_guard<std::mutex> lock(m);
      thread_ids.insert(pthread_self());
    }));
  }
  for (auto& worker : workers) {
    worker.join();
  }
  assert(thread_ids.size() == n);

  return 0;
}

Les pools de threads ont donc toujours un sens. Cela dit, j'ai vu une vidéo où les membres du comité C++ ont discuté des pools de threads en ce qui concerne std::async (IIRC), mais je ne le trouve pas pour le moment.

24
dorserg

UNE std::thread est un fil d'exécution. Période. D'où cela vient, comment il y parvient, s'il y a un pool de threads "réels", etc., tout cela n'est pas pertinent pour la norme. Tant qu'il agit comme un fil, il peut s'agir d'un std::thread.

Maintenant, les chances sont bonnes que std::thread est un thread OS réel, pas quelque chose tiré d'un pool de threads ou autre. Mais C++ 11 permet théoriquement un std::thread à implémenter comme quelque chose tiré d'un pool.

11
Nicol Bolas

std::thread est censé être extrêmement bon marché en termes de coûts d'abstraction, c'est des trucs de bas niveau. Si je comprends bien, les implémentations de bibliothèques standard vont probablement envelopper les mécanismes OS sous-jacents aussi près que possible afin que vous puissiez supposer que la surcharge de la création de threads est similaire ou équivalente.

Je ne connais pas d'implémentations spécifiques mais c'est ma compréhension de seconde main de la lecture C++ Concurrency In Action que la norme suggère qu'ils utilisent la méthode la plus efficace pratique. L'auteur semblait certainement penser que le coût serait plus ou moins négligeable par rapport au bricolage.

La bibliothèque est similaire à Boost conceptuellement, donc j'imagine que l'utilisation de l'implémentation Boost pour tirer des conclusions ne serait pas trop farfelue.

Fondamentalement, je ne pense pas qu'il y ait de réponse à votre question directement parce qu'elle n'est tout simplement pas spécifiée. Bien qu'il me semble que nous serons plus susceptibles de voir des implémentations de wrapper très minces, je ne pense pas que les écrivains de bibliothèque soient restreints d'utiliser des pools de threads si cela offre des avantages en termes d'efficacité.

5
Elliott

Tout d'abord, comme vous l'avez mentionné, la norme C++ ne spécifie essentiellement pas l'implémentation de la bibliothèque. Mais un implémenteur de la bibliothèque standard C++ doit obéir à la règle "comme si".

Par exemple, cela signifie que le constructeur de std::thread doit se comporter comme si nouveau thread créé, qu'il s'agisse d'un wrapper fin de l'API sous-jacente ou d'une implémentation efficace telle que pool de threads . (ici, "thread" signifie un thread d'exécution abstrait dans la spécification C++ 11, pas un thread natif du système d'exploitation)

Sur l'implémentation du pool de threads;

  • Le compilateur et la bibliothèque C++ doivent traiter correctement les ressources spécifiques aux threads C++ (par exemple. thread_local variable), et ils devraient travailler ensemble en coopération lors de l'exécution.
  • Même si la condition ci-dessus a été satisfaite, il semble impossible de coopérer avec des ressources de threads spécifiques au système d'exploitation (TLS pour Windows, TSS pour pthread, etc.).

Donc, je suppose que la plupart des std::thread l'implémentation n'est que le wrapper de l'API de threading sous-jacente.

2
yohjp