16 votes

Suivi de la progression lors de l'utilisation de Parallel.ForEach de TPL

Quelle est la meilleure façon de suivre les progrès dans les domaines suivants

long total = Products.LongCount();
long current = 0;
double Progress = 0.0;

Parallel.ForEach(Products, product =>
{
    try
    {
        var price = GetPrice(SystemAccount, product);
        SavePrice(product,price);
    }
    finally
    {
        Interlocked.Decrement(ref this.current);
    }});

Je veux mettre à jour la variable de progression de 0.0 à 1.0 (courant/total) mais je ne veux pas utiliser quelque chose qui aurait un effet négatif sur le parallélisme.

15voto

svick Points 81772

La solution de Jon est bonne, si vous avez besoin d'une synchronisation simple comme celle-ci, votre première tentative devrait presque toujours utiliser lock . Mais si vous mesurez que le verrouillage ralentit trop les choses, vous devriez penser à utiliser quelque chose comme Interlocked .

Dans ce cas, j'utiliserais Interlocked.Increment pour incrémenter le compte courant, et changer Progress en une propriété :

private long total;
private long current;
public double Progress
{
    get
    {
        if (total == 0)
            return 0;
        return (double)current / total;
    }
}

…

this.total = Products.LongCount();
this.current = 0;

Parallel.ForEach(Products, product =>
{
    try
    {
        var price = GetPrice(SystemAccount, product);
        SavePrice(product, price);
    }
    finally
    {
        Interlocked.Increment(ref this.current);
    }
});

Je ne suis pas sûr que les itérations qui se terminent par une exception doivent être considérées comme terminées.

4voto

Jon Points 194296

Puisque vous n'effectuez que quelques calculs rapides, assurez l'atomicité en verrouillant sur un objet approprié :

long total = Products.LongCount();
long current = 0;
double Progress = 0.0;
var lockTarget = new object();

Parallel.ForEach(Products, product =>
{
    try
    {
        var price = GetPrice(SystemAccount, product);
        SavePrice(product,price);
    }
    finally
    {
        lock (lockTarget) {
            Progress = ++this.current / total;
        }
    }});

2voto

Cédric Bignon Points 6693

Une solution sans utiliser aucun blocage dans le corps :

long total = Products.LongCount();
BlockingCollection<MyState> states = new BlockingCollection<MyState>();

Parallel.ForEach(Products, () =>
{
    MyState myState = new MyState();
    states.Add(myState);
    return myState;
},
(i, state, arg3, myState) =>
{
    try
    {
        var price = GetPrice(SystemAccount, product);
        SavePrice(product,price);
    }
    finally
    {
        myState.value++;
        return myState;
    }
},
i => { }
);

Ensuite, pour accéder à la progression actuelle :

(float)states.Sum(state => state.value) / total

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