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

Windows Presentation Foundation Discussion :

Background worker et objets créés à l'interieur du second thread


Sujet :

Windows Presentation Foundation

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut Background worker et objets créés à l'interieur du second thread
    Bonjour à tous,

    Je fais face à un ptit soucis qui je pense a été traité déjà auparavant mais je ne trouve pas mon bonheur sur le web

    J'utilise un backgroundworker pour charger une liste d'images que je colle ensuite dans des controles picturebox (pour l'exemple, car en fait je travaille en wpf, mais le probleme n'est pas situé à ce niveau).

    Donc voici grosso modo le principe :

    - j'ai 1 Sub qui charge tous les noms de fichiers images dans un arraylist puis appelle le dowork du backgroundworker en lui envoyant la liste en parametre.

    - Dans le dowork, j'ai une boucle qui, pour chaque element de mon arraylist créé un objet (c'est une classe a moi, mais bon, en gros c'est une classe qui a une property pour le chemin de l'image et une autre property qui est l'objet bitmap lui meme).
    A la fin de ma boucle, je me retrouve avec un objet de type List(Of MaClasseImage) que j'envoie dans le e.result

    - Dans le workcompleted, je recupere la liste d'images via le e.result que je caste bien entendu en type List(Of MaClasseImage). avec ce resultat, j'envoie tout ca dans l'interface mais là ca crashe car il me dit qu'il ne peut acceder à un objet créé dans un autre thread.

    Message d'erreur complet : Le thread appelant ne peut pas accéder à cet objet parce qu’un autre thread en est propriétaire.

    Ma question est la suivante : comment faire pour transmettre l'objet d'un thread à un autre ?

    Edit : petite précision : je me situe dans une classe donc je n'ai pas acces au dispatcher de la fenetre comment faire autrement ?

    Merci d'avance
    @ bientot

  2. #2
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 194
    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 194
    Par défaut
    les objets héritant de la classe control doivent etre instancié sur le thread principal, tout le reste peut à priori être fait sur l'autre thread

    un objet de type bitmap, il doit donc pouvoir etre créé sur ton thread

    l'évènement workcompleted est biein relégué dans le thread principal pourtant ...


    instancies tu des controles ?


    une feinte pour aller dans le thread principal depuis une classe de faire application.openforms(0).invoke()
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    En fait j'ai l'impression qu'il y a un schmilblik dans ma classe... Mais plus précisement, dans le fonctionnement global. Voici le principe schématisé (ca va pas etre evident a faire :

    Mon appli avec mes controles -> possede une instance de ma classe projet -> ma classe projet chargeant les images via un backgroundworker -> au retour du backgroundworker, je sette ma property Monprojet.images avec le resultat de mon bgworker -> le binding est censé se mettre à jour sur l'interface via le changement de propriété (car ma classe projet implemente InotifyPropertyChanged).

    Donc je me demande si c'est pas le binding qui vient mettre le souc... (mais là j'ai l'impression que ca touche trop à WPF, donc il faudrait que je fasse déplacer le sujet dans la section adequate ...

  4. #4
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 194
    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 194
    Par défaut
    le mot dispatcher m'avait mis la puce à l'oreille, et tu aurais en effet du préciser que tu étais sur WPF

    en wpf je ne vois pas de backgroundworker, j'en déduis donc que tu as utilisé celui de windows forms

    et entre windows forms et wpf, la notion de thread principal diffère légèrement à priori

    donc ca se trouve l'évènement du backgroundworker n'arrive pas à se remettre sur le thread principal, donc essaye de le faire toi meme

    et en wpf le dispatcher du thread principal est trouvable depuis une classe

    essaye avec un code du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     
    Private Delegate Sub DelegateSubA
     
    Public Sub A
        If Windows.Threading.Dispatcher.CurrentDispatcher IsNot Application.Current.Dispatcher Then
          Dim d As New DelegateSubA(AddressOf A)
          Application.Current.Dispatcher.Invoke(d)
          Exit Sub
        End If
     
        ' traitement
    End Sub
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  5. #5
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    En fait, par rapport aux essais que j'ai déjà faits, le backgroundworker fonctionne très bien avec wpf.
    Néanmoins, de par la strucure globale de ma classe, je me pose des questions.
    En fait il y a une chose que je ne comprend pas avec le backgroundworker :

    Dans la methode DoWork d'un backgroundworker, si on créé un objet, et qu'on le passe dans le result, au moment ou on le recupere dans le RunWorkerCompleted via le e.result, à qui appartient-il ? le thread du backgroundworker ou le thread principal ?
    Comment faire pour etre sur de le balancer sur le thread principal ? avec le dispatcher ? (si oui, j'ai déjà essayé... et ca plante toujours. Malheureusement, je ne peux débugger le truc car a priori c'est dans la partie Framework (et je suis quasi sur que cela vient du refresh du binding objet car tout se deroule bien en pas à pas et c'est seulement quand je quitte la derniere instruction qui doit etre executée que ca me lance une exception du type j'utilise un objet créé dans un autre thread que celui qui doit l'utiliser)

    Je sens que je vais galerer pour trouver !

  6. #6
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 194
    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 194
    Par défaut
    essaye sans backgroundworker à tout hasard, via un thread


    dim th as new system.threading.Thread(adressOf departSub)
    th.Start


    dans tous les cas plutot que de passer un paramètre au démarrage, essaye d'utiliser une variable de classe accessible depuis tous les threads


    ca vient surement du binding, mais ca devrait marcher quand meme
    en instanciant ta variable avant d'appeler le traitement asynchrone, l'instance appartient au thread principal
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  7. #7
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    Je t'avouerai que ça m'est assez difficile
    En effet, j'ai toujours essayé de me mettre aux threads, mais malheureusement je ne comprend toujours pas comment on fait passer un objet d'un thread à l'autre.

    Quand tu me dis :
    essaye d'utiliser une variable de classe accessible depuis tous les threads
    cela veut dire quoi ?

    Je créé une classe dans laquelle j'ai un attribut que je modifie via un thread dans une des methodes de ma classe ? Si oui, je crois que ca va pas aller non plus, l'objet sera créé dans le thread donc logiquement, il va appartenir au thread qui l'a créé non ?

    Mais attend, je suis en train de penser à un truc là, faut que je vois au niveau de la déclaration de mon backgroundworker. Je jette un oeil et je reviens ici si j'ai du nouveau

  8. #8
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 194
    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 194
    Par défaut
    généralités :

    - un objet n'appartient pas réellement à un thread
    - un objet ne peut pas passer d'un thread à un autre, vu qu'il n'appartient pas à un thread
    - seuls les controles posent soucis en inter thread
    - le thread principal est le seul à etre autorisé à modifier des controles, c'est celui qui démarre l'appli
    - depuis un autre thread il est possible de lire une propriété d'un controle mais pas de l'écrire
    - un variable est donc accessible depuis plusieurs threads sans soucis, par contre si un thread fait un for each sur une collection et qu'un autre thread ajoute un élément à la collection ca plante, donc le multithreading impose de se poser quelques questions
    - le backgroundworker n'est qu'un objet qui implémente un thread et des délégués


    un objet ne peut pas passer d'un thread à un autre, c'est une sub qu'on peut faire executer par un autre thread
    en wpf Windows.Threading.Dispatcher.CurrentDispatcher est le thread dans lequel s'execute actuellement le code
    Application.Current.Dispatcher est le thread principal
    si les 2 sont différents ca veut dire qu'on est pas sur le thread principal
    pour passer d'un thread à un autre, il faut utiliser des délégués, et dire à un thread "appelle ce délégué"
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Private Delegate Sub DelegateSubA
     
    Public Sub A
        If Windows.Threading.Dispatcher.CurrentDispatcher IsNot Application.Current.Dispatcher Then
          Dim d As New DelegateSubA(AddressOf A)
          Application.Current.Dispatcher.Invoke(d)
          Exit Sub
        End If
     
        ' traitement
    End Sub
    dans ce code, Private Delegate Sub est une déclaration de délégué
    je teste via isnot si le thread sur lequel je m'execute est le thread principal
    si ce n'est pas le cas, j'instancie un délégué tu type précédement créé, en lui donnant une adresse de sub (la meme ici)
    puis je demande au thread principal (Application.Current.Dispatcher) d'invoker le délégué d
    l'effet en pas en pas à visible, ca remont au début de la sub, mais cette mois ca ne rentre pas dans if, car on est sur le thread principal, donc ca fait le traitement
    à la fin du traitement on revient dans le thread appelant et ca fait le exit sub

    NB:
    - en fait le délégué est mis sur la pile comme les évènements
    - begininvoke permet de mettre le délégué sur la pile et de continuer l'exécution tout de suite, mode asynchrone dans l'asynchrone ^^
    - le délégué et la sub de rappel lui étant associé doivent avoir la meme signature (arguments)
    - la méthode en windows forms est différente, donc je comprends pas comment le backgroundworker pourrait faire, m'enfin je vais regarder comment il est codé
    en windows forms c'est if me.invokerequired et me.invoke; invokerequired (as boolean) indique si on doit faire un invoke, il véréfie lui meme si on est sur le bon thread; cette méthode n'existe que sur la classe control, les autres classes n'ayant pas de soucis de modification par plusieurs threads
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  9. #9
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    Merci pour toutes ces infos ! c'est gentil de te pencher sur mon probleme.

    Alors jusque là, j'avais tout compris. Voici ce que je fais :
    Je vais nommer le tout pour que ce soit le plus compréhensible possible.

    J'ai une classe Ctoto
    J'ai une property Images qui est un List Of ClassPhoto

    J'ai une methode "Charger" dans cette classe :
    La methode charger me créé un arraylist avec les noms des fichiers à charger.
    Ensuite elle appelle l'execution asynchrone de mon backgroundworker en lui passant en parametre la liste de fichiers à charger.

    La methode DoWork du backgroundworker (execution asynchrone) :
    Je créé un new list(of ClassPhoto)
    je boucle sur l'arraylist passé en parametre et pour chaque element, je cree un objet ClassPhoto (en gros, le constructeur prend en parametre le nom du fichier, met ce nom dans une property String "Chemin" et créé un bitmapImage placé dans une property nommée "Image".
    Une fois fini, je retourne la liste de photos via le e.result

    La methode RunWorkerCompleted :
    Je recupere la liste de photos renvoyée dans le e.result
    Je créé une instance de mon delegate vers la sub ChargerLesImages avec pour parametre la liste des photos.

    La sub delegate :
    Dans cette sub je recupere la liste et je sette la propriété Images de ma classe Ctoto.

    Voici ce que je peux constater en mode debug :
    Dans la methode RunWorkerCompleted, je peux explorer e.result sans probleme, j'accede aux objets bitmapimages dans les espions.
    Par contre, dans le délégué je ne peux pas acceder aux objets bitmapImages... bizarre non ?
    Pourtant pour appeler mon délégué je fais comme suis :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, hdlr, args)
    où hdlr est mon instance de mon délégué et args est ma liste d'objets ClassPhoto. C'est vraiment très bizarre !
    Je checké mes classes et aucune ne fait appel à un control quelconque qui pourrait poser probleme de thread concurrent. J'ai plus l'impression que l'objet bitmapimage aurait un comportement de controle ..... o_O

    Ton avis sur le sujet ?

  10. #10
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    Dès fois j'ai envie de me taper moi meme...

    En gros, dans mon thread j'ai oublié de faire img.freeze sur mon objet bitmapImage quand je le charge...

    Mais quel boulet...

  11. #11
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    Citation Envoyé par zax-tfh Voir le message
    Dès fois j'ai envie de me taper moi meme...

    En gros, dans mon thread j'ai oublié de faire img.freeze sur mon objet bitmapImage quand je le charge...

    Mais quel boulet...

    C'est surtout que seuls les objets qui sont freezés, avec la méthode Freeze, peuvent être échangés entre les différents thread de l'application WPF.

  12. #12
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    C'est surtout que seuls les objets qui sont freezés, avec la méthode Freeze, peuvent être échangés entre les différents thread de l'application WPF.
    Quand tu dis ça, tu parle de quels objets ? Car bon, mes objets persos ça fonctionne très bien et je n'utilise pas de freeze dessus.
    Quelle est la différence entre ceux qui ne passent pas entre les threads et ceux qui passent naturellement ?
    BitmapImage n'herite pas de Control pourtant...

    C'est ça qui me met dedans à chaque fois : je n'arrive pas à savoir qui est transmissible et qui ne l'est pas... les controles, ça je comprend qu'ils ne passent pas, mes objets persos, je comprend qu'ils passent, mais ceux qui sont entre deux (ni controles, ni classes persos) mais qui ne passent pas, je ne sais pas les identifier

  13. #13
    Rédacteur
    Avatar de Thomas Lebrun
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    9 161
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 9 161
    Par défaut
    La doc, y'a que ca de vrai

    http://msdn.microsoft.com/en-us/library/ms750509.aspx

    A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot.

  14. #14
    Membre éclairé
    Profil pro
    Inscrit en
    Février 2003
    Messages
    837
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2003
    Messages : 837
    Par défaut
    Je comprend bien ce que tu veux me dire mais cela veut bien dire que pour savoir si tu peux passer d'un thread à l'autre tu dois penser à plusieurs choses :

    - Est-ce un control ?
    - Est-ce un objet Freezable ?
    - Est-ce ceci ?
    - Est-ce cela ?

    Tu vois, c'est cette liste en gros que j'aimerai pouvoir trouver quelque part Ou bien une technique pour etre sur de son coup quand on travaille avec les threads. Comme je debute un peu sur les threads (je dis debute car j'en ai déjà utilisé mais c'est encore assez rare), je ne connais pas la liste exaustive des objets non transmissibles entre threads.

    Tu sais s'il exisite ce genre de liste quelque part ?
    Merci

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

Discussions similaires

  1. arret Background worker thread
    Par ricky78 dans le forum Windows Forms
    Réponses: 3
    Dernier message: 06/02/2007, 12h15
  2. [Plone] Instanciation d'objets créés avec archetype
    Par Beatrix_debutante dans le forum Zope
    Réponses: 3
    Dernier message: 11/10/2006, 17h15
  3. Réponses: 20
    Dernier message: 18/06/2006, 11h44
  4. nombre d'objets créés
    Par akrobat dans le forum C++
    Réponses: 3
    Dernier message: 06/06/2006, 21h07
  5. Réponses: 9
    Dernier message: 31/05/2006, 11h56

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