J'ai commencé à développer une appli qui me permet de pinger au maximum 4 IP différentes pour mes besoins au travail.
Je souhaite que ces IP soient pingées toutes en même temps.
J'avais au début commencé à utiliser 4 backgroundworker. Cela fonctionnait bien mais le problème était que lorsqu'une IP ne répondait pas, le programme "figeait" le temps qu'il ait fini la commande. Les 3 autres ping passaient instantanément puis de nouveau un lag et ainsi de suite.
J'ai donc regardé sur le net, et j'ai vu que je pouvais utiliser 4 threads pour faire ce que je voulais. J'ai donc codé la partie pour 1 seul ping et cela fonctionne (à peu près) bien (j'y reviendrais plus tard en partie 1).
J'ai donc codé la partie pour un deuxième thread en me basant sur le premier code.
Je rencontre plusieurs problèmes :
1 - Lorsque l'IP ne répond pas, j'ai bien un temps d'attente normale d'une ou deux secondes lorsqu'il attend la première réponse. Par contre après, il enchaîne les pings comme s'ils répondaient (toujours en me disant qu'il ne répondent pas)
2 - Après tests, j'ai l'impression qu'il ne me fait pas des pings en simultanés, il les fait quand même les un après les autres et ce n'est pas ce que je veux...
3 - J'ai mis une fonction de stop des ping. Lorsque je relance (c'est une sorte de play / pause), il n'y a que le second ping qui se lance, le premier ne bouge pas, comme si le thread ne se relançait pas...
Voilà à quoi ressemble mon programme :
Les déclarations de mes variables et autres:
Imports System.IO Imports System.Net 'Permet d'ecxécuter des ping et de récupérer les infos Imports System.Net.NetworkInformation 'Permet de récupérer les infos pour les pings Imports System.Threading 'Permet de faire tourner une appli en arrière plan Imports System.Windows.Forms
Public Class Form1 Public h As String 'Nombre d'heures du compteur Public m As String 'Nombre de minutes du compteur Public s As String 'Nombre de secondes du compteur Public Classe As New Class1 'Permet l'accès aux fonctions de Class1 Public Arret As Boolean 'Permet la vérification de la demande d'arrêt du ping Public NbPing As Integer 'Détermine le nombre de ping simultanés demandé Public Ping As New Ping 'Commande pour exécuter le ping Public Rep(3) As PingReply 'Tableau de récupérations des données des ping Public Normal() As String 'tableau des pings normaux Public Erreur(3) As String 'Tableau des erreurs Public Annulation As Boolean 'Vérification de la demande d'annulation du ping
Public Thread1 As New System.Threading.Thread(AddressOf Test) Public Thread2 As New System.Threading.Thread(AddressOf Test2) Public Thread3 As New System.Threading.Thread(AddressOf Test) Public Thread4 As New System.Threading.Thread(AddressOf Test)
Le code lorsque je lance mes pings :
Public Sub StartToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles StartToolStripMenuItem.Click
'Démarrage des pings Dim Thread1 As New System.Threading.Thread(AddressOf Test) Dim Thread2 As New System.Threading.Thread(AddressOf Test2)
Rep(0) = Ping.Send(TextBox1.Text) Thread1.Start()
If TextBox2.Enabled = True Then Rep(1) = Ping.Send(TextBox2.Text) Thread2.Start() End If
End Sub
Le code pour le thread du 1er ping :
Private Sub Test() While Annulation = False AffichagePing1() Thread.Sleep(200) End While End Sub
Le code pour l'affichage du résultat
Public Function AffichagePing1() 'Affichage des résultats Dim Result As String
If Annulation = False Then If Rep(0).Status = 0 Then If Rep(0).RoundtripTime = 0 Then Result = String.Format("Réponse de " & TextBox1.Text & " : temps < 1 ms") Else Result = String.Format("Réponse de " & TextBox1.Text & " : temps = " & Rep(0).RoundtripTime & " ms") End If
RichTextBox1.SelectionColor = Color.Black RichTextBox1.AppendText(Result) RichTextBox1.AppendText(vbNewLine) RichTextBox2.Text = RichTextBox2.Text + 1 Else RichTextBox1.SelectionColor = Color.Red RichTextBox1.AppendText("Temps de réponse dépassé à " & Date.Now.ToLongTimeString) RichTextBox1.AppendText(vbNewLine) RichTextBox3.Text = RichTextBox3.Text + 1 End If
'Force la richbox à être toujours en bas RichTextBox1.ScrollToCaret() End If
End Function
Verriez-vous d'où peuvent venir mes problèmes? J'ai cherché mais en vain...
S'il vous faut d'autres choses, demandez et je vous les ferais parvenir.
@Rykudos : je viens de supprimer ces lignes, même si elles ne sont pas appelées pour le moment, mais j'ai toujours le soucis.
@NHenry : je n'avais pas encore réussi à comprendre cette fonction InvokeRequired (du moins comment l'utiliser), donc j'avais utilisé " CheckForIllegalCrossThreadCalls" et je l'avais mis à false, tout en sachant que ce n'était pas une bonne méthode. Je vais voir si le problème vient de là, je vais me pencher sur la question.
Je vais essayer de voir comment intégrer le "InvokeRequired" et je reviendrais vers vous.
alors mon programme avance, je suis arrivé à utiliser le "Invoke" et j'ai tout ce que je veux.
J'avais une erreur dans mon programme : lorsque je lançais un ping (2ème fenêtre de code), j'ai l'impression qu'il ne lançait qu'un seul ping et qu'après il m'affichait tout le temps la même réponse. J'ai donc modifié mon thread pour que le ping soit fait à chaque boucle :
Private Sub Ping1() Dim Result As String
While Annulation = False Rep(0) = Ping.Send(TextBox1.Text)
If Rep(0).Status = 0 Then If Rep(0).RoundtripTime = 0 Then Result = String.Format("Réponse de " & TextBox1.Text & " : temps < 1 ms" & Chr(10)) Else Result = String.Format("Réponse de " & TextBox1.Text & " : temps = " & Rep(0).RoundtripTime & " ms" & Chr(10)) End If
Me.AffichagePing1(Result) Else Me.AffichagePing1("Temps de réponse dépassé à " & Date.Now.ToLongTimeString & Chr(10)) End If
Thread.Sleep(200) End While End Sub
Voilà le code pour l'affichage :
Private Sub AffichagePing1(ByVal [text] As String)
If Me.RichTextBox1.InvokeRequired Then Dim d As New SetTextCallback(AddressOf AffichagePing1)
If text.Contains("dépassé") Then Me.Invoke(Sub() RichTextBox1.SelectionColor = Color.Red) Me.Invoke(Sub() RichTextBox1.AppendText(text)) Me.Invoke(Sub() RichTextBox3.Text = RichTextBox3.Text + 1) Else Me.Invoke(Sub() RichTextBox1.SelectionColor = Color.Black) Me.Invoke(Sub() RichTextBox1.AppendText(text)) Me.Invoke(Sub() RichTextBox2.Text = RichTextBox2.Text + 1) End If 'Force la RichTextBox à être toujours en bas Me.Invoke(Sub() RichTextBox1.ScrollToCaret()) Else Me.RichTextBox1.AppendText([text]) End If
End Sub
Or maintenant j'ai un nouveau problème : quand je lance un seul ping, tout se passe bien : le programme boucle correctement. Par contre, du moment où je lance un second ping, j'ai une erreur qui apparaît instantanément sur la ligne
If (Ping.Send(TextBox2.Text)).Status = 0 Then
ou sur la ligne
If (Ping.Send(TextBox1.Text)).Status = 0 Then
Une exception non gérée du type 'System.InvalidOperationException' s'est produite dans System.dll
Informations supplémentaires : Un appel asynchrone est déjà en cours. Il doit être mené à terme ou annulé avant que vous ne puissiez appeler cette méthode.
Savez-vous d'où vient le problème et comment je pourrais le régler svp?
J'ai remplacé "textbox1.text" par une variable "IP1" (déclarée en public) qui prend la valeur de la textbox juste avant le lancement du thread. J'ai fait en sorte que le programme n'appelle pas les fonction d'affichage pour voir si le problème viendrait de l'invoke.
Mais toujours le même problème...
Par contre je n'ai pas compris quand tu dis :
If InvokeRequired Then Invoke MaSub Return Else ... Code End If
En gros, quand tu détectes que l'appel est fait à partir d'un thread, tu rappelle la procédure appelée avec les même paramètres, mais en la synchronisant avec le thread d'affichage.
Cela te permet d'avoir seulement un code pour la mise à jour des données, évitant ainsi la duplication de code et les multiples appels à la synchronisation.
Non, mais au lieu d'exécuter 2 bout de code (souvent copié/collé) selon si tu es dans le thread ou pas, tu peux faire le code d'affichage qu'une seule fois, puis si tu es dans le thread de traitement, tu appel la même sub qui a été appellée, mais dans le thread de l'affichage.
P...in je ne comprend rien du tout à ce que tu veux dire NHenrie... Tu peux me montrer un exemple (sans forcément modifier mon code) que je comprenne et que j'adapte.
Par contre j'ai toujours le soucis de l'appel asynchrone que je n'arrive pas à résoudre...
Private Sub AffichagePing1(ByVal [text] As String)
If Me.RichTextBox1.InvokeRequired Then
Dim d As New SetTextCallback(AddressOf AffichagePing1)
If text.Contains("dépassé") Then
Me.Invoke(Sub() RichTextBox1.SelectionColor = Color.Red)
Me.Invoke(Sub() RichTextBox1.AppendText(text))
Me.Invoke(Sub() RichTextBox3.Text = RichTextBox3.Text + 1)
Else
Me.Invoke(Sub() RichTextBox1.SelectionColor = Color.Black)
Me.Invoke(Sub() RichTextBox1.AppendText(text))
Me.Invoke(Sub() RichTextBox2.Text = RichTextBox2.Text + 1)
End If
'Force la RichTextBox à être toujours en bas
Me.Invoke(Sub() RichTextBox1.ScrollToCaret())
Else
Me.RichTextBox1.AppendText([text])
End If
End Sub
Faire :
Private Sub AffichagePing1(ByVal pText As String)
If Me.RichTextBox1.InvokeRequired Then
Me.Invoke(Sub() AffichagePing1(pText)
Else
If pText.Contains("dépassé") Then
RichTextBox1.SelectionColor = Color.Red
RichTextBox1.AppendText(text)
RichTextBox3.Text = RichTextBox3.Text + 1
Else
RichTextBox1.SelectionColor = Color.Black
RichTextBox1.AppendText(text)
RichTextBox2.Text = RichTextBox2.Text + 1
End If
'Force la RichTextBox à être toujours en bas
RichTextBox1.ScrollToCaret()
RichTextBox1.AppendText(pText)
End If
End Sub
Note : J'ai renommé le paramètre le "p" devant signifiant la portée de la variable, ici paramètre, pour les autres portées : l Locale, g Globale, c Classe, i Instance. (par exemple, cela facilite la relecture du code)
Par contre il me reste un problème à régler (pour le moment...), mais le plus important : les pings ne sont pas simultanés... Si un des pings ne répond pas, les autres attendent le retour avant de se lancer.
Une idée de comment faire pour que même si un des pings ne fonctionne pas cela ne soit pas bloquant pour les autres?
Il faut qu'au lancement de ton thread, tu mémorise dans ubne variable locale à ta méthode de thread l'IP à interroger et que même en cas de changement de valeur de la variable d'origine, cela ne change pas l'IP à interroger.
Ensuite, tu lance plusieurs thread en même temps.
Il est aussi possible que ce soit l'API utilisée pour le ping qui empêche une utilisation multithread.