Vous cherchez un petit conseil sur l'utilisation de AsParallel()
ou Parallel.ForEach()
pour accélérer cela.
Voir la méthode que j'ai (simplifiée/bâtarde pour cet exemple) ci-dessous.
Il prend une liste comme "US, FR, APAC", où "APAC" est un alias pour peut-être 50 autres pays "US, FR, JP, IT, GB", etc. La méthode doit prendre "US, FR, APAC" et la convertir en une liste de "US", "FR", plus tous les pays qui se trouvent dans "APAC".
private IEnumerable<string> Countries (string[] countriesAndAliases)
{
var countries = new List<string>();
foreach (var countryOrAlias in countriesAndAliases)
{
if (IsCountryNotAlias(countryOrAlias))
{
countries.Add(countryOrAlias);
}
else
{
foreach (var aliasCountry in AliasCountryLists[countryOrAlias])
{
countries.Add(aliasCountry);
}
}
}
return countries.Distinct();
}
Est-ce que rendre cela parallélisé aussi simple que de le changer pour ce qui est ci-dessous? Y a-t-il plus de nuances à utiliser AsParallel()
que cela? Dois-je utiliser Parallel.ForEach()
au lieu de foreach
? Quelles règles générales dois-je utiliser lors de la parallélisation de boucles foreach
?
private IEnumerable<string> Countries (string[] countriesAndAliases)
{
var countries = new List<string>();
foreach (var countryOrAlias in countriesAndAliases.AsParallel())
{
if (IsCountryNotAlias(countryOrAlias))
{
countries.Add(countryOrAlias);
}
else
{
foreach (var aliasCountry in AliasCountryLists[countryOrAlias].AsParallel())
{
countries.Add(aliasCountry);
}
}
}
return countries.Distinct();
}
Plusieurs points.
écrire simplement countriesAndAliases.AsParallel()
est inutile. AsParallel()
fait partie de la requête Linq qui vient après son exécution en parallèle. La partie est vide, donc pas du tout utile.
en général, vous devez remplacer foreach
par Parallel.ForEach()
. Mais attention au code non thread-safe! Tu l'as. Vous ne pouvez pas simplement l'envelopper dans foreach
car List<T>.Add
n'est pas thread-safe lui-même.
donc vous devriez faire comme ça (désolé, je n'ai pas testé, mais ça compile):
return countriesAndAliases
.AsParallel()
.SelectMany(s =>
IsCountryNotAlias(s)
? Enumerable.Repeat(s,1)
: AliasCountryLists[s]
).Distinct();
Modifier :
Vous devez être sûr de deux autres choses:
IsCountryNotAlias
doit être thread-safe. Ce serait encore mieux si c'est fonction pure .AliasCountryLists
en attendant, car les dictionnaires ne sont pas sûrs pour les threads. Ou utilisez ConcurrentDictionary pour être sûr.Liens utiles qui vous aideront:
Programmation parallèle dans les directives de codage .NET 4
Quand dois-je utiliser Parallel.ForEach? Quand dois-je utiliser PLINQ?
[~ # ~] ps [~ # ~] : Comme vous le voyez, les nouvelles fonctionnalités parallèles ne sont pas aussi évidentes qu'elles le paraissent (et se sentent).
Lorsque vous utilisez AsParallel (), vous devez vous assurer que votre corps est protégé contre les threads. Malheureusement, le code ci-dessus ne fonctionnera pas. List<T>
N'est pas sûr pour les threads, donc votre ajout de AsParallel()
provoquera une condition de concurrence critique.
Si, cependant, vous changez vos collections pour utiliser une collection dans System.Collections.Concurrent , comme ConcurrentBag<T>
, le code ci-dessus fonctionnera très probablement.
Je préférerais utiliser une autre structure de données comme un ensemble pour chaque alias, puis utiliser l'union d'ensemble pour les fusionner.
Quelque chose comme ça
public string[] ExpandAliases(string[] countries){
// Alias definitions
var apac = new HashSet<string> { "US", "FR", ...};
...
var aliases = new HashMap<string, Set<string>> { {"APAC": apac}, ... };
var expanded = new HashSet<string>
foreach(var country in countries){
if(aliases.Contains(country)
expanded.Union(aliases[country]);
else{
expanded.Add(country);
}
return expanded.ToArray();
}
Remarque: le code doit être considéré comme un pseudo-code.
Cela me semble être une opération intrinsèquement sérielle. Tout ce que vous faites, c'est parcourir une liste de chaînes et les insérer dans une autre liste. Les bibliothèques de parallélisation vont le faire, plus un tas de threads et de synchronisation - cela finirait probablement plus lentement.
Vous devez également utiliser un HashSet<string>
si vous ne voulez pas de doublons.