Meilleur auteur de réponses
Comment gérer les Tasks

Question
-
Bonjour
Je ne suis pas un expert mais ai été amené à m’intéresser aux Tasks car le programme que je développe lit et écrit des gros fichiers texte. Ceci amène le programme à ne plus réagir aux click des boutons, aux menus et à la progression de la ProgressBar.
Je crois avoir compris qu'il me fallait introduire des procédures asynchrone pour résoudre ce problème.
Je confirme que je n'ai pas besoin d'effectuer des tâches en parallèle et que celles-ci doivent s'effectuer dans l'ordre de programmation car le résultat de l'une est utilisée par la suivante.
Mes données sont basées sur une classe nommée Toponyme dotée de plusieurs propriétés telles que Nom, Code Postal etc..
L'ensemble de ces toponymes sont regroupés et triés dans une SortedList(Of) avec
J'ai le code suivant
Dim Communes As SortedList(Of String, Toponyme)
Dim tLireFichierSource = Task(Of SortedList(Of String, Toponyme)).
Factory.StartNew(Function()
CommunesSource = LireFichierSource()
Return CommunesSource
End Function)
End Function)
While tLireFichierSource.Status <> TaskStatus.RanToCompletion
End WhileIf tLireFichierSource.Status = TaskStatus.RanToCompletion Then
If CommunesSource.Count = 0 Or CommunesSource Is Nothing Then Exit Sub
End If
Plusieurs questions
1)La procédure LireFichierSource est définie comme synchrone (sans Async) et les fonction de lecture son également synchrone.
Il y a t il une différence de rapidité de traitement si je la transforme en Asynchrone ?
Ou le fait quelle soit appelée dans la tâche tLireFichierSource la rend automatiquement asynchrone?
Cette procédure a été définie comme une fonction pour récupérer son résultat CommunesSource mais si je la défini comme Async la Function n'est pas acceptée.
Comment faire si je dois la rendre asynchrone et lui demander de renvoyer un résultat
2) Comme j'ai besoin d'attendre la fin de l’exécution de la première tâche pour passer à la suivante j'ai fait un test sur le Statut de celle-ci et ai mis une boucle sans fin pour attendre quelle soit terminée! Je suis persuadé que ce n'est surement pas la bonne methode.
J'ai donc essayé avec:
Dim tLireFichierGed As Task = tLireFichierSource.ContinueWith(Sub(antecedent)
LireFichierGed()
End Sub)
Où LireFichierGed est une deuxième procédure de lecture de fichier.
Mais ContinueWith ne semble pas marcher car le programme n'attend pas la fin de la première procédure.
3) Comment mettre à jour une progress bar pour montrer l'avancement d'une procé"dure dans ce contexte.
Merci pour votre aide
Bernard
Réponses
-
Voici un exemple de lecture utilisant des Task. Les fichiers lues sont les listes des communes de France de chaque année dans des fichiers txt.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'Lancement de la lecture du fichier des communes de France 36658 lignes Dim ListesAnnuellesDesCommunes() As String = {"comsimp2015.txt", "comsimp2014.txt", "comsimp2013.txt"} 'On crée 3 Task, une par fichier à scanner Dim TacheLectureFichier(ListesAnnuellesDesCommunes.Length - 1) As Task(Of String) 'Affichage du début des tâches UpdateProgress("Lancement de la lecture des fichiers de 36658 lignes", 0) 'Création des tâches For ctr As Integer = 0 To ListesAnnuellesDesCommunes.Length - 1 Dim s As String = ListesAnnuellesDesCommunes(ctr) TacheLectureFichier(ctr) = New Task(Of String)(Function() ' Nombre de communes Dim nbCommunes As Integer = 0 ' Nom du fichier des communes de France. Dim NomFichier As String = Application.StartupPath & "\" & s Dim sr As New StreamReader(NomFichier) 'Dim input As String = sr.ReadToEndAsync().Result Dim LigneLue As String Dim ListeDesCommunes As String = "", splitLigne() As String Do While sr.Peek() >= 0 'Lecture ligne à ligne pour "ralentir" le process et illustrer les task et ProgressBar LigneLue = sr.ReadLine() splitLigne = LigneLue.Split(vbTab) ListeDesCommunes &= ";" & splitLigne(9) Loop sr.Close() sr.Close() 'nWords = Regex.Matches(input, pattern).Count Return ListeDesCommunes End Function) Next ' Vérification que les fichiers existent avant de lancer les tâches Dim allExist As Boolean = True For Each Annee In ListesAnnuellesDesCommunes Dim fn As String = Application.StartupPath & "\" & Annee If Not File.Exists(fn) Then allExist = False MessageBox.Show("Ne trouve pas '{0}'", fn) Exit For End If Next ' Lancement des 3 tâches avec ProgressBar If allExist Then 'For Each t In TacheLectureFichier ' t.Start() 'Next TacheLectureFichier(0).Start() TacheLectureFichier(0).Wait() 'Test de complétion: inutile mais cela vérifie que tout est ok If TacheLectureFichier(0).IsCompleted = True Then UpdateProgress("Tâche 1 :" & ListesAnnuellesDesCommunes(0), CInt(100 * 1 / 3)) Me.Refresh() 'DoEvents est déconseillé, mais dans ce cas cela permet de récupérer la lenteur du ProgressBar Application.DoEvents() End If TacheLectureFichier(1).Start() TacheLectureFichier(1).Wait() If TacheLectureFichier(1).IsCompleted = True Then UpdateProgress("Tâche 2 :" & ListesAnnuellesDesCommunes(1), CInt(100 * 2 / 3)) Me.Refresh() Application.DoEvents() End If TacheLectureFichier(2).Start() TacheLectureFichier(2).Wait() If TacheLectureFichier(2).IsCompleted = True Then UpdateProgress("Tâche 3 :" & ListesAnnuellesDesCommunes(2), CInt(100 * 3 / 3)) Me.Refresh() Application.DoEvents() End If 'Task.WaitAll(TacheLectureFichier) For ctr As Integer = 0 To ListesAnnuellesDesCommunes.Length - 1 Debug.Print("Année " & ListesAnnuellesDesCommunes(ctr)) Debug.Print(TacheLectureFichier(ctr).Result) Next End If End Sub
J'ai également bloqué le Thread qui affiche la ProgressBar avec Thread.Sleep(1000) car la barre se rafraichie trop lentement pour l'affichage...
Private Delegate Sub UpdateProgressDelegate(ByVal strMsg As String, ByVal intPercentage As Integer) Public Sub UpdateProgress(ByVal strMsg As String, ByVal intPercentage As Integer) If Me.InvokeRequired Then Me.Invoke(New UpdateProgressDelegate(AddressOf UpdateProgress), New Object() {strMsg, intPercentage}) Else If strMsg <> "" Then Me.Label2.Visible = True Me.Label2.Text = strMsg End If If intPercentage >= Me.ProgressBar1.Minimum AndAlso intPercentage <= Me.ProgressBar1.Maximum Then Me.ProgressBar1.Value = intPercentage Me.ProgressBar1.Update() Me.Refresh() Threading.Thread.Sleep(1000) End If End If 'Peut être que le DoEvents serait mieux ici que dans la sub appelante? 'System.Windows.Forms.Application.DoEvents() End Sub
Je pense que cela illustre des solutions à vos problèmes. Si oui indiquez que c'est une solution.
Cyrille Precetti
- Marqué comme réponse Teodora SharkovaModerator mardi 22 décembre 2015 09:58
-
Cf ma réponse ici pour éviter une redondance
Richard Clark
Consultant - Formateur .NET
http://www.c2i.fr
Depuis 1996: le 1er site .NET francophone- Marqué comme réponse Teodora SharkovaModerator mardi 22 décembre 2015 09:58
Toutes les réponses
-
Plusieurs questions à la fois et complexe.
Question 3: Mais voici un exemple de ProgressBar:
C'est fait avec le contrôle ProgressBar et avec un Delegate si sur une forme de message qui informe sur votre tâche principale:
Private Delegate Sub UpdateProgressDelegate(ByVal strMsg As String, ByVal intPercentage As Integer) Public Sub UpdateProgress(ByVal strMsg As String, ByVal intPercentage As Integer) If Me.InvokeRequired Then Me.Invoke(New UpdateProgressDelegate(AddressOf UpdateProgress), New Object() {strMsg, intPercentage}) Else If strMsg <> "" Then Me.Label2.Visible = True Me.Label2.Text = strMsg End If If intPercentage >= Me.ProgressBar1.Minimum AndAlso intPercentage <= Me.ProgressBar1.Maximum Then Me.ProgressBar1.Value = intPercentage End If End If System.Windows.Forms.Application.DoEvents() End Sub
Pour les lectures de fichiers asynchrone, la classe System.Threading.Task semble correcte. Il me semble que lorsque que vous lancer une Task cela se passe de façon asynchrone (Question 1: Asynchrone par définition)
Question 2: Pour renvoyer des valeurs, utilisez la classe Task(Of TResult) (https://msdn.microsoft.com/en-us/library/dd321424%28v=vs.110%29.aspx)
Cyrille Precetti
Votez et marquez comme utile si cela vous a servi...- Modifié Cyrille Précetti lundi 21 décembre 2015 14:42
-
Bonjour et merci pour votre aide.
Mais j'ai besoin d'en savoir plus!
Merci pour la progressBar.
Quelle est la réponse à ma question :
La procédure LireFichierSource est définie comme synchrone (sans Async) et les fonction de lecture son également synchrone. Il y a t il une différence de rapidité de traitement si je la transforme en Asynchrone ?
OUi j'ai bien vu dans l'aide MSDN qu'il fallait utiliser Task(Of TResult) pour un retour de résultat, mais je n'ai pas réussi à trouver la bonne syntaxe pour un objet retourné = SortedList(Of String, Toponymes)
Encore merci
Bernard
-
Re bonjour
J'ai implanté la procédure concernant la ProgressBar
Le programme s'arrete à la ligne :
Me.Invoke(New UpdateProgressDelegate(AddressOf UpdateProgress),
New Object() {strMsg, intPercentage})sans message d'erreur et bloque le programme
Pour le premier appel strMsg ="" et intPercentage=0
Merci
Bernard
-
Le processus Me.Invoke attend que le Thread principal soit inoccupé pour reprendre son action.
Si le Invoke bloque c'est qu'ailleurs le Thread principal est bloqué en attente de quelque chose.
Pour valider votre code de ProgressBar vous devez découpler tout le reste et simuler l'avancement de vos tâches. Lorsque vous aurez validé cela recouplez les autres tâches et aller voir ce qui bloque le Thread principal, probablement une attente de vos Task.
Un des points probable est le code que vous avez montré:
While tLireFichierSource.Status <> TaskStatus.RanToCompletion End While
Testez si le RanToCompletion fini bien....
Cyrille Precetti
-
Voici un exemple de lecture utilisant des Task. Les fichiers lues sont les listes des communes de France de chaque année dans des fichiers txt.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 'Lancement de la lecture du fichier des communes de France 36658 lignes Dim ListesAnnuellesDesCommunes() As String = {"comsimp2015.txt", "comsimp2014.txt", "comsimp2013.txt"} 'On crée 3 Task, une par fichier à scanner Dim TacheLectureFichier(ListesAnnuellesDesCommunes.Length - 1) As Task(Of String) 'Affichage du début des tâches UpdateProgress("Lancement de la lecture des fichiers de 36658 lignes", 0) 'Création des tâches For ctr As Integer = 0 To ListesAnnuellesDesCommunes.Length - 1 Dim s As String = ListesAnnuellesDesCommunes(ctr) TacheLectureFichier(ctr) = New Task(Of String)(Function() ' Nombre de communes Dim nbCommunes As Integer = 0 ' Nom du fichier des communes de France. Dim NomFichier As String = Application.StartupPath & "\" & s Dim sr As New StreamReader(NomFichier) 'Dim input As String = sr.ReadToEndAsync().Result Dim LigneLue As String Dim ListeDesCommunes As String = "", splitLigne() As String Do While sr.Peek() >= 0 'Lecture ligne à ligne pour "ralentir" le process et illustrer les task et ProgressBar LigneLue = sr.ReadLine() splitLigne = LigneLue.Split(vbTab) ListeDesCommunes &= ";" & splitLigne(9) Loop sr.Close() sr.Close() 'nWords = Regex.Matches(input, pattern).Count Return ListeDesCommunes End Function) Next ' Vérification que les fichiers existent avant de lancer les tâches Dim allExist As Boolean = True For Each Annee In ListesAnnuellesDesCommunes Dim fn As String = Application.StartupPath & "\" & Annee If Not File.Exists(fn) Then allExist = False MessageBox.Show("Ne trouve pas '{0}'", fn) Exit For End If Next ' Lancement des 3 tâches avec ProgressBar If allExist Then 'For Each t In TacheLectureFichier ' t.Start() 'Next TacheLectureFichier(0).Start() TacheLectureFichier(0).Wait() 'Test de complétion: inutile mais cela vérifie que tout est ok If TacheLectureFichier(0).IsCompleted = True Then UpdateProgress("Tâche 1 :" & ListesAnnuellesDesCommunes(0), CInt(100 * 1 / 3)) Me.Refresh() 'DoEvents est déconseillé, mais dans ce cas cela permet de récupérer la lenteur du ProgressBar Application.DoEvents() End If TacheLectureFichier(1).Start() TacheLectureFichier(1).Wait() If TacheLectureFichier(1).IsCompleted = True Then UpdateProgress("Tâche 2 :" & ListesAnnuellesDesCommunes(1), CInt(100 * 2 / 3)) Me.Refresh() Application.DoEvents() End If TacheLectureFichier(2).Start() TacheLectureFichier(2).Wait() If TacheLectureFichier(2).IsCompleted = True Then UpdateProgress("Tâche 3 :" & ListesAnnuellesDesCommunes(2), CInt(100 * 3 / 3)) Me.Refresh() Application.DoEvents() End If 'Task.WaitAll(TacheLectureFichier) For ctr As Integer = 0 To ListesAnnuellesDesCommunes.Length - 1 Debug.Print("Année " & ListesAnnuellesDesCommunes(ctr)) Debug.Print(TacheLectureFichier(ctr).Result) Next End If End Sub
J'ai également bloqué le Thread qui affiche la ProgressBar avec Thread.Sleep(1000) car la barre se rafraichie trop lentement pour l'affichage...
Private Delegate Sub UpdateProgressDelegate(ByVal strMsg As String, ByVal intPercentage As Integer) Public Sub UpdateProgress(ByVal strMsg As String, ByVal intPercentage As Integer) If Me.InvokeRequired Then Me.Invoke(New UpdateProgressDelegate(AddressOf UpdateProgress), New Object() {strMsg, intPercentage}) Else If strMsg <> "" Then Me.Label2.Visible = True Me.Label2.Text = strMsg End If If intPercentage >= Me.ProgressBar1.Minimum AndAlso intPercentage <= Me.ProgressBar1.Maximum Then Me.ProgressBar1.Value = intPercentage Me.ProgressBar1.Update() Me.Refresh() Threading.Thread.Sleep(1000) End If End If 'Peut être que le DoEvents serait mieux ici que dans la sub appelante? 'System.Windows.Forms.Application.DoEvents() End Sub
Je pense que cela illustre des solutions à vos problèmes. Si oui indiquez que c'est une solution.
Cyrille Precetti
- Marqué comme réponse Teodora SharkovaModerator mardi 22 décembre 2015 09:58
-
Cf ma réponse ici pour éviter une redondance
Richard Clark
Consultant - Formateur .NET
http://www.c2i.fr
Depuis 1996: le 1er site .NET francophone- Marqué comme réponse Teodora SharkovaModerator mardi 22 décembre 2015 09:58
-
-
-
Ca fait plaisir d'avoir des remerciements ;-)
Richard Clark
Consultant - Formateur .NET
http://www.c2i.fr
Depuis 1996: le 1er site .NET francophone