2010-07-19 5 views
6

Considérons cet exemple:Utilisation emboîtées Parallel.For

var x = 0; 

for (var i = 0; i < 100; i++) 
{ 
    for (var a = i+1; a < 100; a++) 
     x += 1; 
} 

Lors de l'impression x nous toujours get 4950. Qu'en est-il si je veux paralléliser cela?

C'est ce que je viens avec

Parallel.For(0, 100, i => Parallel.For(i + 1, 100, a => { x += 1; })); 

Cependant cela n'imprime pas 4950 chaque fois que je le lance. Pourquoi?

+1

peut-être vous devriez verrouiller x – onof

+0

@onof, comment feriez-vous cela? –

+2

Parallèle.Pour (0, 100, i => Parallèle.Pour (i + 1, 100, a => {verrou (x) {x + = 1;}})); – onof

Répondre

12

Parallel Extensions vous aide dans la création de tâches, l'allocation, l'exécution et le rendez-vous, dans une syntaxe quasi-impérative. Ce qu'il ne fait pas est de prendre soin de toutes sortes de sécurité de fil (l'un des pitfalls). Vous essayez de faire en sorte que les threads parallèles mettent simultanément à jour une seule variable partagée. Pour faire quelque chose comme ça correctement, vous devez introduire par ex. verrouillage.

Je ne suis pas sûr de ce que vous essayez de faire. Je suppose que votre code est juste un espace réservé ou une expérience. La parallélisation ne convient que lorsque vous pouvez isoler vos différents travaux. pas quand vous avez constamment rendez-vous avec des données partagées.

+0

Les données que j'ai sont seulement lues, il y a juste une variable (collection) à laquelle j'ajoute des résultats, et je me fiche de l'ordre dans lequel elles ont été ajoutées ici. Verrouiller un mécanizm lent? –

+1

Le verrouillage extrême peut vous donner des performances ** plus mauvaises que la simple implémentation séquentielle. Envisagez d'utiliser, par exemple, 'Parallel.Aggregate()' pour combiner vos résultats à la place. Regardez par exemple http://msdn.microsoft.com/en-us/magazine/cc163340.aspx. –

0

Comme alternative au verrouillage à chaque fois, vous pouvez utiliser fil-variables locales en combinaison avec une serrure:

Object thisLock = new Object(); 
var globalSum = 0; 
System.Threading.Tasks.Parallel.For(0, 100, i => { 
    System.Threading.Tasks.Parallel.For(i + 1, 100,() => 0, (num, loopState, subSum) => ++subSum, subSum => { 
     lock(thisLock) { globalSum += subSum; } 
    }); 
}); 

Console.WriteLine(globalSum); 
+0

La mise en forme de ceci est difficile à lire. –

+1

@MattSmith Ouais désolé, il a été un peu foiré lors de la copie à partir de Visual Studio :) va le réparer – Christian

2

serait ici la « bonne » façon de le faire, ce ne serait pas vous obliger à verrouillez votre objet total final et vous demanderez seulement de faire les opérations interverrouillées à la fin de la boucle de chaque thread local.

int x = 0; 
Parallel.For(0, 100, 
    () => 0, //LocalInit 
    (i, loopstate, outerlocal) => 
    { 
     Parallel.For(i + 1, 100, 
      () => 0, //LocalInit 
      (a, loopState, innerLocal) => { return innerLocal + 1; }, 
      (innerLocal) => Interlocked.Add(ref outerlocal, innerLocal)); //Local Final 
     return outerlocal; 
    }, 
    (outerLocal) => Interlocked.Add(ref x, outerLocal)); //Local Final 

Cependant, ayant deux déclarations Parallel imbriquées qui font ce petit travail est probablement une mauvaise idée. Il y a des frais généraux qui doivent être pris en compte, si vous faites une si petite quantité de travail il serait préférable de faire une seule déclaration Parallel ou n'en avoir aucune.

Je vous recommande fortement d'aller télécharger et lire Patterns for Parallel Programming, il va dans le bon détail sur pourquoi de petites boucles parallèles imbriquées comme ceci ne sont pas une bonne idée.

Questions connexes