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 :

Portée d'une variable depuis un Thread secondaire [Débutant]


Sujet :

VB.NET

  1. #1
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut Portée d'une variable depuis un Thread secondaire
    Bonjour a tous,
    J'espère ne pas vous induire en erreur avec le titre de mon post. Voici mon problème :
    J’exécute une grosse opération depuis un BackgroundWorker (scrutation d'une arborescence à la recherche de certains fichiers selon certains critères et quand le ou les fichiers sont trouvés je mets a jour une table 'Paths' avec inscription du chemin trouvé). Pour m'éviter des effets indésirables, je désactive a ce moment là certains événements dans ma procédure ActiveControlesHandler, notamment ceux liés aux tables.
    Cette procédure fonctionne très bien sauf depuis mon BackgroundWorker depuis lequel je n'arrive pas a désactiver les événements. Voici mes constations après avoir placé plusieurs points d'arrêts et fait du pas a pas :
    Ma variable 'ControleHanddlerIsActive' est égale a False chaque fois que je rentre dans la procédure depuis mon BackgroundWorker alors qu'au lancement du programme cette variable a été correctement initialisé a True et les événements activés.
    Mieux qu'un long discours voici l'extrait de mon code :
    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
    Public Class frmModPaths
    Dim ControleHanddlerIsActive As Boolean
     
        Public Sub ActiveControlesHandler(ByVal State As Boolean)
     
            If State = True And ControleHanddlerIsActive = False Then
                AddHandler DataGridView1.CurrentCellDirtyStateChanged, AddressOf EventCurrentCellDirtyChanged
                AddHandler MainBindingSourcePaths.CurrentItemChanged, AddressOf EventbsCurrentItemChanged
                AddHandler MainBindingSourcePaths.CurrentChanged, AddressOf EventbsCurrentChanged
                AddHandler MainBindingSourcePaths.PositionChanged, AddressOf EventbsPositionChanged
                AddHandler dtTablePaths.ColumnChanging, AddressOf EventTableColumnChanging
                AddHandler dtTablePaths.ColumnChanged, AddressOf EventTableColumnChanged
                AddHandler dtTablePaths.TableNewRow, AddressOf EventTableNewRow
     
                ControleHanddlerIsActive = True
            ElseIf State = False And ControleHanddlerIsActive = True Then
                RemoveHandler DataGridView1.CurrentCellDirtyStateChanged, AddressOf EventCurrentCellDirtyChanged
                RemoveHandler MainBindingSourcePaths.CurrentItemChanged, AddressOf EventbsCurrentItemChanged
                RemoveHandler MainBindingSourcePaths.CurrentChanged, AddressOf EventbsCurrentChanged
                RemoveHandler MainBindingSourcePaths.PositionChanged, AddressOf EventbsPositionChanged
                RemoveHandler dtTablePaths.ColumnChanging, AddressOf EventTableColumnChanging
                RemoveHandler dtTablePaths.ColumnChanged, AddressOf EventTableColumnChanged
                RemoveHandler dtTablePaths.TableNewRow, AddressOf EventTableNewRow
     
               ControleHanddlerIsActive = False
            End If
     
        End Sub
    End Class
    Les lignes executées depuis mon BackGroundWorker :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    frmModPaths.ActiveControlesHandler(False)
    ' .....Opérations de mise a jour de la table 'Paths'
    frmModPaths.ActiveControlesHandler(True)
    Merci a tous pour votre aide.

  2. #2
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Personne pour m'aider ? Je ne comprends vraiment pas pourquoi ma variable ControleHanddlerIsActive n'est pas accessible et non partagé par le thread du BackGroundWorker. Sans cette désactivation des événements j'ai des appels récursifs sur l’événement ColumnChanging, l'architecture de mon programme est certainement un peu bancale mais je ne peux pas remmetre en cause cette architecture aujourd'hui, ça me demanderait trop de travail. Bref c'est le bazard

    Sinon je serais aussi preneur pour une solution de contournement.

    Voici la ligne dans le code d'actualisation de la base qui me lève un événement indésirable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim Row As DataRow = dt.NewRow()
    Je travail avec un dataset et le but est d'actualiser la table 'Paths' de ce Dataset en tache de fond tout en permettant a l'utilisateur de travailler.

    Si quelqu'un a une idée ça m'aiderais beaucoup.

  3. #3
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir, j'ai trouvé une solution. C'est franchement pas très propre mais ça fonctionne a peu prêt.
    Je désactive les événements avant de rentrer le BackGroundWorker autrement dit depuis le Thread du programme principal et je réactive les événements lorsque le BackgroundWorker a terminé son travail. Seul soucis l'utilisateur peut aussi indirectement activer ou désactiver ces événements lors d'une saisie. Pour régler ce problème j'ai rajouté ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Public Sub ActiveControlesHandler(ByVal State As Boolean)
    If BackGroundWorker1.IsBusy Then Exit Sub
    '.......suite
    End Sub
    Par contre si l'utilisateur fait des saisies dans la table 'Paths' au moment ou s'execute le BackGroundWorker, la saisie ne sera pas prise en compte mais normalement l'utilisateur ne devrait faire des saisies dans la table Paths que de façon exceptionnelle.
    Je fait du code pas terrible en ce moment
    J’hésite a passer en résolue mais on va dire que c'est bon .

  4. #4
    Membre expert
    Avatar de GuruuMeditation
    Homme Profil pro
    .Net Architect
    Inscrit en
    Octobre 2010
    Messages
    1 705
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : Belgique

    Informations professionnelles :
    Activité : .Net Architect
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2010
    Messages : 1 705
    Points : 3 568
    Points
    3 568
    Par défaut
    Bizarre ton worker devrait avoir accès à ta variable. tu es sur que tu n'instancie pas un autre frmModPaths?
    Microsoft MVP : Windows Platform

    MCPD - Windows Phone Developer
    MCPD - Windows Developer 4

    http://www.guruumeditation.net

    “If debugging is the process of removing bugs, then programming must be the process of putting them in.”
    (Edsger W. Dijkstra)

  5. #5
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Bonjour. Je peux garantir que ce n'est pas un problème de parallélisme : il n'existe aucune portée sémantique associée à un thread. En C# tous les threads voient les mêmes variables avec les mêmes valeurs.

    Deux petites exceptions dans lesquelles un champ statique peut avoir une valeur différente dans chaque thread:
    * Les champs marqués avec ThreadStaticAttribute.
    * Les applications utilisant plusieurs domaines d'applications : chaque domaine a sa propre valeur. Plus généralement les domaines d'applications restreignent la communication entre eux si bien que rien ne peut être partagé et tout doit être dupliqué, comme entre plusieurs processus.


    Enfin il est normal que l'on ne puisse pas altérer l'état du formulaire depuis un autre thread que le principal : chaque contrôle est lié au thread sur lequel il a été créé et seul ce dernier peut le modifier. Un autre thread devra passer par Control.Invoke / BeginInvoke. C'est une façon d'assurer la synchronisation.

  6. #6
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonjour et merci pour vos réponses, je vais regarder cette histoire de plus prêt car même si j'ai résolu mon problème, apriori vous me confirmez que ce n'est pas trés normal de ne pas pouvoir acceder a cette variable de facon partagée.
    @GuruuMeditation : Je n'instancie pas mes formulaires, je travaille directement avec la classe (en statique) mais je verifirais quand même ce soir si je ne dit pas de bétises.

  7. #7
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir a tous, je mets un petit exemple pour mettre en évidence l'accessibilité des variables entre Threads. Après avoir fait beaucoup d'essais, en fait on s'appercois que le comportement est très différent suivant si on accède aux variables du formulaire courant ou si accède aux variables d'un autres formulaire. Dans le 1er cas les variables sont bien partagées entre Threads, dans le second cas elles ne le sont pas. C'est ce 2eme cas que j'ai mis en exemple ci-dessous :
    Je n'ai pas un assez bon niveau pour dire si ce comportement est normal ou pas mais ça me parait interressant d'exposer le problème.
    Peut-être que quelqu'un aura une explication beaucoup plus claire que moi car j'avoue que je m'y perds un peu.


    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
    Imports System.ComponentModel
     
    Public Class Form1
        Dim BgW As New BackgroundWorker
     
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            'Nomme le Thread du programme Principale
            Threading.Thread.CurrentThread.Name = "MainThread"
            Form2.Show()
            AddHandler BgW.DoWork, AddressOf DoWork
            AddHandler BgW.ProgressChanged, AddressOf ProgressChanged
            AddHandler BgW.RunWorkerCompleted, AddressOf RunWorkerCompleted
        End Sub
     
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            BgW.RunWorkerAsync()
        End Sub
     
        Private Sub DoWork()
            Threading.Thread.CurrentThread.Name = "BgWThread"
            ' Form2.VariablePublicATester = "From Form1"
            Form2.TestVariable()
        End Sub
        Private Sub ProgressChanged()
     
        End Sub
        Private Sub RunWorkerCompleted()
     
        End Sub
    End Class
     
    Public Class Form2
        Public VariablePublicATester As String
        Dim VariablePrivateATester As String
        Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ' Initialise VariableATester
            VariablePublicATester = "From Form2"
            VariablePrivateATester = "From Form2"
            TestVariable()
        End Sub
        Public Sub TestVariable()
            Dim Threadname As String = Threading.Thread.CurrentThread.Name
     
            MessageBox.Show("Thread courant : " & Threadname & Environment.NewLine & "VariablePublic = " & VariablePublicATester & Environment.NewLine & "VariablePrivé = " & VariablePrivateATester)
        End Sub
    End Class

  8. #8
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Bonjour. Encore une fois j'affirme, et ça ne fait aucun doute, que tous les threads voient les mêmes valeurs au même moment.

    Concernant le code fourni, il est incomplet et tu ne dis pas quel est le résultat. Mais à vue de nez TestVariable est exécuté une fois depuis Form1_Load et l'autre fois depuis Form2_Load, alors que les variables testées ne sont iniitialisées que dans Form2_Load. Donc je parie que Form2_Load n'avait pas encore été exécuté la première fois car elle est chargée après form1.

    Bref, rien à voir avec les threads, comme je le disais.

  9. #9
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Désolé, j'aurrais du fournir le résultat et je ne remets pas votre parole en doute, j'essaye juste de comprendre.
    (Les variables sont inititialisées au démarrage du programme autrement dit, juste aprés l'appel de Form2.show).
    A ce moment là le MessageBox m'affiche :
    Thread Courant : MainThread
    VariablePublic = "From Form2"
    VariablePrivé = "From Form2"
    Cela montre que mes variables sont bien initialisées.

    Ensuite je clique sur Button1 pour lancer le BackgroundWorker et j'obtiens ce résultat :
    Thread Courant : BgWThread
    VariablePublic =
    VariablePrivé =
    Cela montre que mes variables ne contiennent plus les valeurs precedements initialisées.

    Dans la procedure ci-dessous, si j'appelle directement Form2.TestVariable au lieu de lancer le BackGroundWorker je retrouve les bonnes valeurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
            'BgW.RunWorkerAsync()
            Form2.TestVariable()
     End Sub

  10. #10
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par BasicZX81 Voir le message
    Désolé, j'aurrais du fournir le résultat et je ne remets pas votre parole en doute, j'essaye juste de comprendre.
    Ce n'était pas un reproche et je salue cette attitude cartésienne. Je n'avais insisté que pour ne pas laisser place au doute.

    Cela étant dit je ne peux pas expliquer le résultat. Je sais que l'interprétation est fausse mais je ne peux pas dire quelle est la bonne. A mon avis Form2 désigne une instance différente la seconde fois mais je ne peux pas expliquer pourquoi. Il se pourrait que ce soit une histoire de modèle d'appartement hérité de VB6 mais il me semblait que ça n'avait plus cours (un mode de compatibilité est-il activé ?) et je connais trop peu VB.

  11. #11
    Membre averti
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Mars 2012
    Messages
    640
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Bâtiment

    Informations forums :
    Inscription : Mars 2012
    Messages : 640
    Points : 372
    Points
    372
    Par défaut
    Bonsoir a tous, pour ceux qui ont suivie la discussion, je vous apporte une autre constatation intéressante. (Malgré tout, je reste un peu sur ma faim car je n'ai pas trouvé de véritables explications ni de règles qui régissent ce comportement).
    Donc voilà : Lorsque l'on instancie le formulaire Form2 les variables se retrouvent cette fois accessible depuis le backgroundWorker.
    Comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Public Class Form1
    Dim f As New Form2
    ......
    End Class
    J'ai toujours pensé que ça ne servait a rien d'instancier les formulaires dans le cas ou on a pas besoin de plusieurs instances. Visiblement c'était une erreur de pensez cela.
    @DonQuiche : Je n'ai pas activé un mode de compatibilité, j'ai juste crée un nouveau projet de type formulaire classique.

    Enfin il est normal que l'on ne puisse pas altérer l'état du formulaire depuis un autre thread que le principal : chaque contrôle est lié au thread sur lequel il a été créé et seul ce dernier peut le modifier. Un autre thread devra passer par Control.Invoke / BeginInvoke. C'est une façon d'assurer la synchronisation.
    Pour info, j'ai réussi a modifier la variable Privé du Formulaire Form2 depuis le backGroundWorker mais je conçois que ce que je fait n'est peut-être pas tout a fait secure (bien que je ne fasse pas appel aux contrôles du formulaire mais a des variables).

  12. #12
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par BasicZX81 Voir le message
    J'ai toujours pensé que ça ne servait a rien d'instancier les formulaires dans le cas ou on a pas besoin de plusieurs instances. Visiblement c'était une erreur de pensez cela.
    Mais un formulaire est toujours instancié.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/12/2006, 22h55
  2. Portée d'une variable dans une boucle FOR ?
    Par Neo41 dans le forum C++
    Réponses: 20
    Dernier message: 17/11/2006, 11h14
  3. [XSLT] pb portée d'une variable
    Par NPortmann dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 23/05/2006, 15h53
  4. Portée d'une variable globale
    Par Giill dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 27/12/2005, 10h13
  5. Réponses: 5
    Dernier message: 08/09/2005, 20h33

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