35 votes

Parallel.For (): Mettre à jour la variable en dehors de la boucle

Je regarde juste les nouvelles fonctionnalités .NET 4.0. Avec cela, j'essaie un calcul simple en utilisant Parallel.For et une boucle normale for(x;x;x) .

Cependant, j'obtiens des résultats différents environ 50% du temps.

 long sum = 0;

Parallel.For(1, 10000, y =>
    {
        sum += y;
    }
);

Console.WriteLine(sum.ToString());

sum = 0;

for (int y = 1; y < 10000; y++)
{
   sum += y;
}
Console.WriteLine(sum.ToString());
 

Je suppose que les threads essaient de mettre à jour "sum" en même temps.
Y a-t-il un moyen évident de contourner cela?

69voto

Ade Miller Points 7750

Vous ne pouvez pas faire cela. sum est partagé entre vous threads parallèles. Vous devez vous assurer que l' sum variable est uniquement accessible par un seul thread à la fois:

// DON'T DO THIS!
Parallel.For(0, data.Count, i =>
{
    Interlocked.Add(ref sum, data[i]);
});

MAIS... C'est un anti-modèle, car vous avez effectivement sérialisés la boucle parce que chaque thread se bloque sur l' Interlocked.Add.

Ce que vous devez faire est d'ajouter des sous-totaux et les fusionner à la fin comme ceci:

Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
    {
        subtotal += result[i];
        return subtotal;
    },
    (x) => Interlocked.Add(ref sum, x)
);

Vous trouverez de plus amples discussions sur la MSDN: http://msdn.microsoft.com/en-us/library/dd460703.aspx

PLUG: Vous pouvez trouver plus sur ce sujet dans le Chapitre 2 sur Un Guide pour la Programmation Parallèle

La suite est aussi certainement en valeur une lecture...

Les modèles de Programmation Parallèle: la Compréhension et l'Application de Modèles Parallèles avec la .NET Framework 4 - Stephen Toub

17voto

sum += y; est en fait sum = sum + y;. Vous obtenez des résultats erronés en raison de la course suivante à condition de:

  1. Thread1 lectures sum
  2. Thread2 lectures sum
  3. Thread1 calcule sum+y1, et stocke le résultat dans sum
  4. Thread2 calcule sum+y2, et stocke le résultat dans sum

sum est maintenant égal à sum+y2, au lieu de sum+y1+y2.

5voto

SLaks Points 391154

Votre conjecturer est correct.

Lorsque vous écrivez sum += y, le moteur d'exécution est le suivant:

  1. Lire la rubrique sur la pile
  2. Ajouter y pour la pile
  3. Écrire le résultat dans le champ

Si deux threads de lire le champ, dans le même temps, la modification apportée par le premier thread sera remplacé par le deuxième thread.

Vous devez utiliser Interlocked.Add, qui effectue l'addition comme une seule opération atomique.

4voto

Eric Mickelsen Points 6332

L'incrémentation d'une longue n'est pas une opération atomique.

4voto

Mgetz Points 2630

Je pense qu'il est important de distinguer que cette boucle n'est pas capable d'être partitionné pour le parallélisme, car comme il a été mentionné ci-dessus chaque itération de la boucle dépend de l'avant. Le parallèle a pour but explicite des tâches parallèles, telles que les pixels de mise à l'échelle, etc. parce que chaque itération de la boucle ne peut pas avoir de dépendances de données en dehors de son itération.

Parallel.For(0, input.length, x =>
{
    output[x] = input[x] * scalingFactor;
});

Ci-dessus un exemple de code qui permet de facile de partitionnement pour le parallélisme. Cependant, un mot d'avertissement, le parallélisme est livré avec un prix, même la boucle que j'ai utilisé comme exemple est loin beaucoup trop simple de s'embêter avec un parallèle parce que le temps prend plus de temps que le temps gagné par le parallélisme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X