IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

VB.NET Discussion :

Utilisation de Synclock


Sujet :

VB.NET

  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut Utilisation de Synclock
    Bonjour,

    J'aimerais optimiser une de mes application pour le multithread,

    Le soucis qui se pose c'est que (je n'ai pas essayé) si plusieurs thread exécutant un délégué afin de faire la mise à jour d'une datagridview

    example de délégué :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
        Public Sub ajout()
            Dim tableau As String()
            tableau = datas.Split(":")
            DataGridView1.Rows.Add(tableau)
        End Sub
    Si cette procédure est appelée par plusieurs thread en même temps, je dois utiliser SyncLock non ?

    Autrement j'aimerais savoir si c'est possible possible d'ajouter des éléments à une collection sans utiliser SyncLock

    Je n'ai jamais utilisé SyncLock donc je ne sais pas si c'est ça que je dois utiliser pour pallier a mon éventuel problème ?

    Merci

  2. #2
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 193
    Par défaut
    oui synclock bloque les différents thread
    le premier thread a passé à l'endroit du synclock franchit la barrière
    tant qu'il n'est pas sorti (end synclock), tous les autres threads attendent devant la porte (le thread qui a eut le droit de passer peut repasser au meme endroit sans soucis)
    une fois qu'il a fini, le thread suivant (le 2eme arrivé) rentre, les autres attendent encore

    sur une collection, si un thread fait un for each pendant qu'un autre fait .Add, ca plante, donc le synclock est en théorie nécessaire
    mais dans ce cas précis, un readerwriterlock serait plus approprié, voir meme le slimreaderwriterlock (nouveau sur le framework 3 ou 3.5)
    ca gagne un peu de perf par rapport au synclock
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Citation Envoyé par sperot51 Voir le message
    oui synclock bloque les différents thread
    le premier thread a passé à l'endroit du synclock franchit la barrière
    tant qu'il n'est pas sorti (end synclock), tous les autres threads attendent devant la porte (le thread qui a eut le droit de passer peut repasser au meme endroit sans soucis)
    une fois qu'il a fini, le thread suivant (le 2eme arrivé) rentre, les autres attendent encore

    sur une collection, si un thread fait un for each pendant qu'un autre fait .Add, ca plante, donc le synclock est en théorie nécessaire
    mais dans ce cas précis, un readerwriterlock serait plus approprié, voir meme le slimreaderwriterlock (nouveau sur le framework 3 ou 3.5)
    ca gagne un peu de perf par rapport au synclock
    Merci pour les infos

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Il est dit sur msdn :
    N'utilisez pas l'instruction SyncLock pour verrouiller des threads qui manipulent des contrôles ou des formulaires. Comme les méthodes des contrôles et formulaires rappellent parfois une procédure d'appel, vous risquez de créer sans vous en rendre compte un blocage ; dans cette situation, deux threads attendent chacun que l'autre annule le verrou, ce qui entraîne l'arrêt de l'application.
    comment je dois faire alors ?

  5. #5
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 193
    Par défaut
    suffit de pas verrouiller des morceaux de code qui utilisent des controles ... m'enfin en théorie on met des sub à part qu'on appelle via des délégués ...

    tu es confronté au problème ?
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Désolé je n'avais encore rien essayé, maintenant j'ai effectivement fait des essais, synclock marche bien comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Dim lock As New Object
        Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
            SyncLock lock
                Dim index As Integer = 0
                While Col(index).EstFait And index < Col.Count - 1 And index < Col.Count - 1
                    index += 1
                End While
                Col(index).SetData(f.DataDL(tb1, Col(index).GetNumero, tb2, CheckBox1.Checked))
                Me.Invoke(DelegueAjouterligne)
            End SyncLock
        End Sub
    J'ai comme ceci 5 backgroundworker

    A noter que au lieu d'utiliser un délégué nous aurions pu utiliser l'évènement RunWorkerCompleted du backgroundworker dans ce cas

    j'ai quand même un soucis quand j'essaye de faire ce traitement

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    Delegate Sub SimpleDelegate()
    Dim DelegueMajDGV As New SimpleDelegate(AddressOf majDGV)
        Public Sub majDGV()
            DataGridView1.Rows.Clear()
            For Each unel As Ligne In Col
                DataGridView1.Rows.Add(unel.GetData)
            Next
        End Sub
    J'essaye de l'appeler avec un délégué mais ca ne passe jamais, même dans un synclock, même en mettant mon code dans l'évènement terminé du BGW avec un synclock.

    A noter que pour l'instant j'utilise un compteur et met a jour me.text à la place de la mise à jour de la datagridview, donc il semble que le problème soit le foreach ou l'accès à la list


    (HS): j'ai remarqué un bug étrange que j'avais déjà remarqué avec des applications (que je pensais avant être un bug courant)
    Admettons que dans mon interface graphique j'ai un bouton et au clique j'appelle ma procédure majDGV, l'interface ne répondra plus sauf si je fais clic droit sur le programme dans la barre des tâches ( a chaque clic le gui se mettra à jour)

  7. #7
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 193
    Par défaut
    on ne voit pas l'appel du délégué DelegueMajDGV dans ton code


    "ca ne passe jamais" ce n'est pas explicite ...



    un synclock sur un backgroundworker soit c'est inutile, soit c'est un bug d'analyse (ou de compréhension de synclock)


    et quand on clic sur un bouton, l'évènement est sur le thread principal, donc si tu fais un traitement long (ou un while true pour tester) ca bloque le thread principal, hors c'est lui qui met à jour l'interface donc normal que ca fige (c'est pour ca quil faut utiliser des threads (ou donc des backgroundworker))
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Citation Envoyé par sperot51 Voir le message
    on ne voit pas l'appel du délégué DelegueMajDGV dans ton code


    "ca ne passe jamais" ce n'est pas explicite ...



    un synclock sur un backgroundworker soit c'est inutile, soit c'est un bug d'analyse (ou de compréhension de synclock)


    et quand on clic sur un bouton, l'évènement est sur le thread principal, donc si tu fais un traitement long (ou un while true pour tester) ca bloque le thread principal, hors c'est lui qui met à jour l'interface donc normal que ca fige (c'est pour ca quil faut utiliser des threads (ou donc des backgroundworker))
    si je créer un thread supplémentaire pour la mise à jour il demandera une mise à jour de la datagridview alors que la datagridview est sur le programme principal

    Désolé comme j'ai fait des essais j'ai changé les nom de mes variables

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
        Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
            SyncLock lock
                Dim index As Integer = 0
                While Col(index).EstFait And index < Col.Count - 1 And index < Col.Count - 1
                    index += 1
                End While
                Col(index).SetData(f.DataDL(tb1, Col(index).GetNumero, tb2, CheckBox1.Checked))
                Me.Invoke(DelegueAjouterligne)
            End SyncLock
        End Sub
    Dans chacun de mes backgroundworker je fais ca (invoke delegueajouterligne)

    et pour l'instant dans la procédure appelée j'ai ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        Delegate Sub SimpleDelegate()
        Dim DelegueAjouterligne As New SimpleDelegate(AddressOf ajoutligne)
        Public Sub ajoutligne()
            compteur += 1
            Me.Text = compteur
        End Sub
    ca fonctionne

    Je triche un peu en appelant les backgroundworker lorsqu'ils sont finis (j'ai réfléchi a plusieurs facon de faire ce que je veux faire mais celle ci me semblait être la mieux pour pouvoir arrêter et reprendre)

    En fait il s'agit de téléchargements, j'utilise plusieurs thread pour télécharger simultanément plusieurs fichiers, c'est pour ca que j'en ai mis 5

    j'avais également regarder l'utilisation des readerwriterlock et slim mais j'ai lu sur un blog qu'il n'était pas adapter lorsqu'on veut pouvoir mettre en pause/annuler un thread

    Ca m'embête de ne pouvoir rien afficher avant la fin du chargement des 100~10000 fichiers, là mon appli fonctionne très bien, je peux aussi voir l'avancement directement dans me.text ( un peu archaïque j'aurais pu mettre un label ) mais la mise à jour de ma datagridview impossible j'ai pourtant essayé:


    -un délégué pointant sur une procédure faisant la mise à jour (la procédure fonctionne lorsque tout est terminé donc elle fonctionne) appelé avec me.invoke dans un backgroundworker (je n'ai pas essayé avec les 5 )
    résultat :
    sans synclock, "La valeur ne peut pas être null. Nom du paramètre : values" sur invoke, je ne comprends pas
    avec synclock le gui plante complètement (je n'ai pas trop chargé les données donc la datagridview aurait du s'afficher très rapidement) le gui ne plante pas si je créer un autre lock et refait l'erreur comme si il n'y avait pas de synclock (logique) (c'est bien le lock utilisé par les 5 backgroundworker


    -l'appel direct de ma procédure (de mise a jour) dans une procédure lié à l'évenement backgroundworker fini
    résultat : sans synclock mon application plante dénonçant la pile des appels
    avec synclock le gui freeze

  9. #9
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 193
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 193
    Par défaut
    normalement on instancie pas un délégué au niveau d'une variable de classe, mais avant chaque invoke (ou au moins dans chaque sub et donc thread dans ton cas)

    de plus le but du backgroundworker et de ne pas avoir à écrire de délégué, il a un évènement progress ramené sur le thread principal qui sert en théorie à faire passer l'avancement, mais tu peux faire passer ce que tu veux dedans (donc les données à afficher dans le dgv)
    et meme avec 5 backgroundworker qui lisent des données, tu peux tous leur dire d'appeler l'avancement, le thread principal gérant une piles des évènements



    sinon ce que tu veux faire me semble pas trop mal finalement
    à part que passer sur le thread principal à chaque ligne, c'est pas super performant, il vaudrait mieux attendre d'avoir quelques dizaines de lignes dans une collection avant d'appeler le thread pour qu'il ajoute toutes ces lignes d'un coup
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  10. #10
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Citation Envoyé par sperot51 Voir le message
    normalement on instancie pas un délégué au niveau d'une variable de classe, mais avant chaque invoke (ou au moins dans chaque sub et donc thread dans ton cas)

    de plus le but du backgroundworker et de ne pas avoir à écrire de délégué, il a un évènement progress ramené sur le thread principal qui sert en théorie à faire passer l'avancement, mais tu peux faire passer ce que tu veux dedans (donc les données à afficher dans le dgv)
    et meme avec 5 backgroundworker qui lisent des données, tu peux tous leur dire d'appeler l'avancement, le thread principal gérant une piles des évènements



    sinon ce que tu veux faire me semble pas trop mal finalement
    à part que passer sur le thread principal à chaque ligne, c'est pas super performant, il vaudrait mieux attendre d'avoir quelques dizaines de lignes dans une collection avant d'appeler le thread pour qu'il ajoute toutes ces lignes d'un coup
    Merci pour ces infos,

    j'ai déjà fait une appli ou j'utilise la progression, j'avais pensé à l'idée de faire passer la ligne qui vient d'être traitée mais je pensais utiliser un délégué sinon pour faire passer les ligne (comme tu dis).

    j'ai aussi penser a faire un composant parce que 5 bgw avec le meme code dedans ca fait lourd, mais je n'ai pas réussi, mais ca ne va pas régler mon problème, je vais faire d'autres essais

  11. #11
    Membre actif
    Profil pro
    Inscrit en
    Décembre 2008
    Messages
    91
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2008
    Messages : 91
    Par défaut
    Finalement je suis revenu a une façon de faire plus "classique"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
           Dim i As Integer
            For i = 0 (pour le bgw1, 1 pour le bgw2 ... jusqu'a 10) To max Step 10
                Dim s As String()
                s = f.DataDL(tb1, i, tb2, maison)
                If String.Compare(s(0), "Impossible") = 0 Then
     
                Else
                    Col.Add(New Ligne(i, s))
                End If
                Me.Invoke(DelegueAjouterligne)
            Next
    Je n'ai pas encore fait ce que tu m'as dit (utiliser l'évènement progresschanged pour lancer la maj) je voulais voir les performances de cette version

    Pensez vous que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    My.Computer.Network.DownloadFile(url, fichier)
    empêche son accès pour le multithread ? logiquement non mais je me pose des questions j'obtiens de meilleures performance avec une fonction trouvée sur internet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
     
    Shared Function Download(ByVal url As String) As String
            ' Déclaration des variables
            Dim LeDomaine As Integer
            Dim LeURL As String = ""
            Dim LeHost As String = ""
            Dim LeChemin As String = ""
            Dim Resultat As String = ""
            Dim RecupHTTPChaine As String = ""
            Dim WebClient As New System.Net.Sockets.TcpClient
            Dim WebStream As NetworkStream
            Dim WebWriter As StreamWriter
            Dim WebReader As StreamReader
            ' On découpe l'url envoyée en paramètre à la fonction
            LeDomaine = InStr(UCase(url), "HTTP://")
            If LeDomaine > 0 Then
                LeURL = Mid(url, LeDomaine + 7)
            Else
                LeURL = url
            End If
            LeDomaine = InStr(LeURL, "/")
            If LeDomaine > 0 Then
                LeHost = Mid(LeURL, 1, LeDomaine - 1)
                LeChemin = Mid(LeURL, LeDomaine)
            Else
                LeHost = LeURL
                LeChemin = "/"
            End If
     
            ' On construit notre requete HTTP
            RecupHTTPChaine = "GET " & LeChemin & " HTTP/1.1" & vbCrLf & "Host: " & LeHost & vbCrLf & "Connection: Close" & vbCrLf & vbCrLf
     
            ' On ouvre une socket sur le port 80
            WebClient.Connect(LeHost, 80)
            WebStream = WebClient.GetStream
            WebWriter = New StreamWriter(WebStream)
            WebWriter.Write(RecupHTTPChaine)
            WebWriter.Flush()
            WebReader = New StreamReader(WebStream)
            ' On stock la page html dans notre variable "Resultat"
            Resultat = WebReader.ReadToEnd()
     
            ' On ferme la socket
            WebStream.Close()
            WebStream = Nothing
            WebClient.Close()
            WebClient = Nothing
            WebWriter.Close()
            WebWriter = Nothing
            WebReader.Close()
            WebReader = Nothing
     
            ' On renvoi ce que l'on a récupéré
            Download = Resultat
        End Function
    Enfin je crois,

    J'ai remarqué quelquechose d'intéressant, si vous utilisez des synclock pour prendre des données par exemple, pour votre traitement principale, il est plus avantageux de créer un autre lock pour l'affichage, de cette facon votre traitement ne sera pas encombré par l'affichage

    Edit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
        Shared Function mydownload(ByVal url As String) As String
            Dim r As WebRequest = Net.WebRequest.Create(url)
            Dim dataStream As Stream = r.GetResponse.GetResponseStream()
            Dim reader As New StreamReader(dataStream)
            Dim responseFromServer As String = reader.ReadToEnd()
            reader.Close()
            dataStream.Close()
            Return responseFromServer
        End Function
    Je viens de presque faire cette fonction et ca fonctionne, après faut voir laquelle des 3 est la plus rapide/accepte le multithread

Discussions similaires

  1. utiliser les tag [MFC] [Win32] [.NET] [C++/CLI]
    Par hiko-seijuro dans le forum Visual C++
    Réponses: 8
    Dernier message: 08/06/2005, 15h57
  2. Réponses: 4
    Dernier message: 05/06/2002, 14h35
  3. utilisation du meta type ANY
    Par Anonymous dans le forum CORBA
    Réponses: 1
    Dernier message: 15/04/2002, 12h36
  4. [BCB5] Utilisation des Ressources (.res)
    Par Vince78 dans le forum C++Builder
    Réponses: 2
    Dernier message: 04/04/2002, 16h01
  5. Réponses: 2
    Dernier message: 20/03/2002, 23h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo