2010-06-05 7 views
1

code:Augmentez le filetage/les mutex, pourquoi cela fonctionne-t-il?

#include <iostream> 
#include "stdafx.h" 
#include <boost/thread.hpp> 
#include <boost/thread/mutex.hpp> 

using namespace std; 
boost::mutex mut; 
double results[10]; 

void doubler(int x) { 
//boost::mutex::scoped_lock lck(mut); 
results[x] = x*2; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
boost::thread_group thds; 
for (int x = 10; x>0; x--) { 
    boost::thread *Thread = new boost::thread(&doubler, x); 
    thds.add_thread(Thread); 
} 

thds.join_all(); 

for (int x = 0; x<10; x++) { 
    cout << results[x] << endl; 
} 

return 0; 
} 

Sortie:

0 
2 
4 
6 
8 
10 
12 
14 
16 
18 
Press any key to continue . . . 

... Alors ma question est pourquoi cela fonctionne (pour autant que je peux dire, je l'ai couru environ 20 fois), la production de ce qui précède sortie, même avec le verrouillage commenté? Je pensais que l'idée générale était:

 
in each thread: 
calculate 2*x 
copy results to CPU register(s) 
store calculation in correct part of array 
copy results back to main(shared) memory 

Je pense que dans toutes les conditions, mais parfait, cela aurait pour résultat une partie de la matrice de résultats ayant 0 valeurs. Copie-t-il seulement le double requis du tableau dans un registre cpu? Ou est-ce juste trop court d'un calcul pour être préempté avant d'écrire le résultat en RAM? Merci.

Répondre

3

L'affectation a une valeur lvalue de type double à gauche et cette valeur lvalue est le seul objet auquel accède un thread. Puisque chaque thread accède à un objet différent, il n'y a pas de course de données.

Notez que l'inscription d'un tableau ne constitue pas un accès.

+0

@Flamewires: Les structures de données compliquées sont correctes tant que chaque thread n'agit que sur sa propre structure (comme dans le tableau). Si vous retournez des bits sur un nombre entier, vous devrez soit verrouiller, soit vous assurer que vous avez une lecture atomique et un ensemble. Dans ce cas simple, cela forcerait tout le thread à fonctionner en série avec une forte contention pour le verrou, ce qui en ferait un très mauvais candidat pour la parallélisation. –

+0

Oui, si vous avez remplacé le double par un autre type, ça irait toujours bien. Il n'y a pas de taille minimale en ce qui concerne le C++ 0x (il appartient à l'implémentation de faire ce travail sur toutes les plateformes). Si vous commencez à jouer avec des bits, vous obtenez une course de données (et donc un comportement indéfini), car vous accèderiez au même objet à partir de plusieurs threads sans synchronisation. – avakar

+0

Ah d'accord, ça aide un peu. – Flamewires

1

Cela fonctionne en raison de la ligne thds.join_all();. Le thread d'exécution principal retient ici jusqu'à ce que tous les autres threads soient terminés, puis continue à imprimer le tableau. Par conséquent, vous savez que toutes les valeurs de tableau ont été stockées avant de les imprimer. Si vous commentez cette ligne, vous obtiendrez des résultats imprévisibles.

+0

Est-ce que cela provoque l'exécution séquentielle de tous les threads? Je pensais rejoindre tout juste requis tous les threads à se terminer avant de continuer, indépendamment de ce qu'ils ont fait avec le tableau de résultats alors qu'ils étaient tous en cours d'exécution. Je ne vois toujours pas pourquoi il n'y a pas de condition de concurrence. – Flamewires

+0

@Flamewires: il n'y a pas de condition de concurrence car le thread principal attend explicitement que les autres threads de travail "rattrapent", éliminant ainsi la condition de concurrence. De plus, étant donné que chaque thread de travail ne met à jour qu'un emplacement spécifiquement affecté en mémoire, il n'est pas nécessaire de s'inquiéter des données. Ainsi, tous les threads de travail font leur travail (attribuez une valeur à leur emplacement de mémoire prédéfini) pendant que le thread principal attend. Lorsque tous les travailleurs ont terminé, le fil principal continue. Les opérateurs travaillent donc en parallèle, mais l'appel join_all() crée un point de séquence garantissant que toutes les données sont dans un bon état. –

+0

Ouais, je comprends cela mais que se passe-t-il si chacun a été assigné, disons, un peu dans un entier? au lieu d'un double dans un tableau? Voyez-vous ce que j'obtiens? Sûrement une condition de concurrence existerait car pour accéder à ce bit, l'entier entier serait copié, mis à jour, puis remplacé par X nombre de threads en même temps. Quelle est la limite inférieure de la taille d'un emplacement de mémoire que je peux désigner et ne pas provoquer une condition de concurrence? un mot? – Flamewires