Variable locale de méthode en cours de modification dans un Parallel.For.Dans quelle mesure est-ce sûr pour les threads ?
-
21-12-2019 - |
Question
J'ai cet extrait de code :
int totalData = result.Data.Count;
int count = 0;
Parallel.ForEach(result.Data, data =>
{
try
{
EventRange importedEntity = ImportEntity(auxResult.EntityName, data);
count++;
EntityImported(importedEntity, count, totalData);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
EntityImported est un événement qui devrait indiquer combien d'entités ont déjà été traitées et combien d'entités je dois traiter.Mes inquiétudes concernent la sécurité des threads lors de l'incrémentation du nombre à l'intérieur du lambda et les étapes que vous recommanderiez pour garantir que l'événement est toujours déclenché avec la valeur correcte de la variable count.
La solution
Ce n'est pas du tout thread-safe pour le moment.
Vous pouvez utiliser Interlocked.Increment(ref count)
à la place, mais c'est en général mieux vaut avoir une valeur "locale" pour chaque thread, et les additionner à la fin.De cette façon, vous n'avez besoin d'aucun flux de données inter-threads, autre que l'allocation des éléments à traiter.
Il y a une surcharge de Parallel.ForEach
conçu uniquement dans ce but - regardez l'exemple en bas de la documentation, car il fait quelque chose très similaire à votre code.(Il maintient un décompte local, puis additionne les décomptes à la fin.) Malheureusement, cela n'aide pas dans votre cas particulier en raison de la façon dont vous devez déclencher un événement à chaque itération - mais généralement c'est une meilleure approche.
Dans ce cas, vous devez utiliser le résultat de Interlocked.Increment
dans votre événement, soulevant le code :
Parallel.ForEach(result.Data, data =>
{
try
{
EventRange importedEntity = ImportEntity(auxResult.EntityName, data);
int newCount = Interlocked.Increment(ref count);
EntityImported(importedEntity, newCount, totalData);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
De cette façon, il y aura exactement un événement déclenché pour chaque compte (donc un avec 0, un avec 1, un avec 2, etc.).