Ping et multithreading

Signaler
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016
-
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
-
Bonjour à tous,

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.

Merci d'avance

Jérôme

7 réponses

Pour les thread 1,3 et 4 tu appelle le même sub cela ne viendrai pas de la ?
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
Je ne sais pas si c'est la cause, mais déjà :
AffichagePing1 est exécuté dans un thread mais intervient sur l'IHM.
Il serait préférable d'utiliser les appels cross-thread.
http://msdn.microsoft.com/en-us/library/ms171728%28v=vs.85%29.aspx

(Partie vers : if (this.textBox1.InvokeRequired) )
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

Bonjour et merci pour vos réponses.

@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.

Merci en tout cas
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

Bonjour,

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?

Merci d'avance
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
If Me.RichTextBox1.InvokeRequired Then
...
If text.Contains("dépassé") Then
...
Else


Si InvokeRequired=True, il en faut pas appeler directement le contrôle.
De base, il est préférable de faire :
(Sub nommé "MaSub")
If InvokeRequired Then
 Invoke MaSub
 Return
Else
... Code
End If


Même remarque concernant :
Rep(0) = Ping.Send(TextBox1.Text)

Le mieux serait d'utiliser une variable locale qui mémorise l'IP à interroger et qui ne fasse ensuite appel à l'IHM que pour afficher le résultat.
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

Pour la ligne :
Rep(0) = Ping.Send(TextBox1.Text)

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


Tu peux expliquer stp?

Merci
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
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.
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

Tu veux dire que les fonctions "AffichagePingX" je peux en faire une seule qui se chargera d'afficher tout?

Et sinon pour le message d'erreur lorsque je lance plusieurs ping tu as une idée?
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
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.
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

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...

Merci
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
Pour faire plus claire, à la place de
	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)
Messages postés
14
Date d'inscription
jeudi 29 juillet 2004
Statut
Membre
Dernière intervention
22 août 2016

Ok merci j'ai compris :)

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?

Merci
Messages postés
14800
Date d'inscription
vendredi 14 mars 2003
Statut
Modérateur
Dernière intervention
19 juin 2021
156
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.