Merci à tous pour vos différentes aides qui m'aident, mais surtout qui m'apprennent beaucoup.
Vos explications sont tellement plus parlantes pour moi que ce que je trouve sur les sites "généraux".
A mon avis le Task.Run est en trop. Le Parallel.Foreach() n'en a pas besoin.
Je ne sais pas si c'est la meilleure façon de faire, mais j'ai mis le Task.Run car la méthode est un async Task qui doit retourner, entre autre, une progression.
En effet le Parallel.Foreach() n'a pas besoin de Task.Run, mais pour pouvoir retourner la progression après chaque itération du Parallel.Foreach(), il faut que le Parallel.Foreach() soit dans un await afin que la progression puisse être retournée après chaque itération et pas après le Parallel.Foreach() complet.
Le invoke ou begininvoke ne me paraît pas utile dans le cas présent parce que je ne change rien dans le Threat de l'UI. Comme cette méthode doit se trouver dans une bibliothèque, je ne sais pas a priori comment sera gérée la progression (le nom de la progressbar à alimenter peut être différent dans différents projets faisant appel à la bibliothèque, par exemple). Il n'y a donc pas d'affichage de point, ou de texte ou quoi que ce soit d'autre. Je retourne juste une progression et l'appelant gère la progression comme il le veut. Je ne fais donc que lui retourner une progression de l'avancée de la création des points (qui est gérée par l'UI appelante), puis la liste des points à la fin. Actuellement sur un projet test, la ProgressBar avance bien au fur et à mesure de la création des points.
J'espère ne pas dire de bêtise, mais je crois bien que les objets List<T> ne sont pas thread-safe (en fait, j'en suis sûr).
La commande points.Add(newPoint); va subir une race condition entre les threads générés par le Parallel.ForEach, et il y a un risque de ne pas retourner les bonnes valeurs. Et comme c'est la base du retour de ta fonction, autant le signaler.
Il vaut mieux se tourner vers un ImmutableList<T>, qui est thread-safe, ou implémenter une gestion de la synchronisation (Immutable, c'est bien...)
Je ne connaissais pas le ImmutableList que j'ai installé (Nuget). Par contre new ImmutableList<T>(int longueurDeLaListe) ne semble pas exister. J'ai dont bricolé
ImmutableList<Pt> points = new List<Pt>(dtPoints.Rows.Count).ToImmutableList();
qui ne me semble pas très propre visuellement mais qui a l'air de fonctionner.
Je me doutait que je pouvais avoir des problèmes avec l'alimentation de ma List, c'est pourquoi j'avais fais
1 2 3 4 5 6 7
| if (newPoint != null)
{
lock (obj)
{
points.Add(newPoint);
}
} |
car je me disais qu'en "lockant" la liste, elle ne pourrait pas être alimentée par 2 Threat en même temps, mais que les Threat devraient attendre leur tour. Peut-être étais-je dans l'erreur ou, ce à quoi je pensais, ça ralenti l'alimentation de la liste si des Threat doivent attendre.
En conclusion, encore merci à tous de votre aide et de vos explications.
La situation actuelle de la méthode est
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public static async Task<ObservableCollection<Pt>> ImportPointAsync(int srid, string mdbPath, IProgress<int> progress)
{
DataTable dtPoints = new DataTable();
DataTable dtPCodes = new DataTable();
--- chargement des tables à partir de la mdb ---
if (dtPoints == null || dtPoints.Rows.Count == 0 || dtPCodes == null || dtPCodes.Rows.Count == 0)
return null;
ImmutableList<Pt> points = new List<Pt>(dtPoints.Rows.Count).ToImmutableList();
int totalCount = 0, count = 1, i=0;
Object obj = new Object();
var maListeDeListesDePoints = dtPoints.Rows.OfType<System.Data.DataRow>().GroupBy(x => i++ / 5).Select(x => x.ToList()).ToList(); // Constitue des listes de 5 points
totalCount = maListeDeListesDePoints.Count;
await Task.Run(() =>
Parallel.ForEach(maListeDeListesDePoints, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount / 2 }, listeDePoints => {
for (int j=0; j< listeDePoints.Count; j++)
{
DataRow drPoint = listeDePoints[j];
DataRow[] drsPCodes = dtPCodes.Select("IdPoint = " + drPoint["IdPoint"]);
List<int> lstPCodes = new List<int>();
foreach (DataRow drPCode in drsPCodes)
{
int pcode = 0;
if (drPCode != null && int.TryParse(drPCode["PCode"].ToString(), out pcode))
lstPCodes.Add(pcode);
}
Pt newPoint = new Pt(drPoint["nomPoint"].ToString(), (double)drPoint["PointX"], (double)drPoint["PointY"], lstPCodes, srid);
if (newPoint != null)
points.Add(newPoint);
}
lock (obj)
{
if (progress != null)
{
count++;
progress.Report((count * 100 / totalCount));
}
}
})
);
if (points != null && points.Count > 0)
return new ObservableCollection<Pt>(points.Distinct());
return null;
} |
Partager