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

C# Discussion :

[best practices][thread] retrouver l'UI


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Par défaut [best practices][thread] retrouver l'UI
    Bonjour,

    Je recherche les meilleures pratiques sur la question du multi-thread avec form.
    J'ai déjà lu plei nde tuto, mais ils traitent toujours de cas simples, tout dans la même classe, etc.
    j'ai une classe qui va se charger de tâches en background qui ne sera pas Form1.
    je cherche a mettre en place le systeme le plus propre possible pour depuis cette classe appeller une methode de Form1 (en "delegate")
    exemple,
    soit la classe Form1 :
    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
        public partial class Form1 : Form
        {
            background worker;
            public Form1()
            {
                InitializeComponent();
                worker = new background();
            }
     
            private void button1_Click(object sender, EventArgs e)
            {   
                worker.setup(par1.Text, par2.Text);             // prépare les element pour le thread
     
                // lance le nouveau thread.
                Thread thread = new Thread(new ThreadStart(worker.start));
                thread.IsBackground=true;
                thread.Start();
            }
            public void display(string info) // met à jour l'UI
            {
                Label1.Text=info;
            }
    et ma classe background :
    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
     public class background
        {
            public readonly object verrou = new object();
     
            delegate void strParamDelegate(string info);
     
            private string p1, p2; // parametres pour le thread worker.
     
            public void setup(string par1, string par2) // appel unique (avant de lancer le thread)
            {
                p1 = par1;
                p2 = par2;
            }
     
            public void start() // thread "worker"
            {
                updateui(""); //par exemple
                Thread.Sleep(1000);
            }
     
            // met à jour l'interface utilisateur
            private void updateui(string info)
            {
                BeginInvoke(new strParamDelegate(XXXX.display), new object[]{info}); //delegate asynchrone
            }
        }
    Voilà, mon probleme est "par quoi je remplace XXXX ?"
    je pourrais mettre la methode display en static (mais c'est trés crade)
    je pourrais utiliser une variable globale faisant réference à ma form instanciée, mais bof
    je pourrai passer une ref de l'instance du form instancié dans la classe background par l'intermediare de la methode setup()...

    Bref, j'aimerai bien avoir qqchose de propre, et séparer la classe form de la classe de traitement...

    Merci pour vos conseils.

  2. #2
    Expert confirmé

    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Septembre 2006
    Messages
    3 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Septembre 2006
    Messages : 3 580
    Par défaut
    bonjour

    pourquoi ne pas passer un delegate à ta classe background et ce delegate sera ainsi un delegate de gestion de la données et pourra provenir
    de chez n'importe qui ?

  3. #3
    Membre éclairé
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Par défaut
    Citation Envoyé par theMonz31 Voir le message
    pourquoi ne pas passer un delegate à ta classe background ?
    T'as 3 ou 4 lignes de code pour que je voie le concept ?
    Merci.

  4. #4
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    En gros tu remplaces la méthode "updateui" de "background" par un champ "updateui" de type Action qui sera fourni via le constructeur de "background".


    Cela dit tu devrais te renseigner sur async/await, histoire d'avoir un code du type (sur l'UI) :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    async void OnButtonClick(....)
    {
       this.Text = "Veuillez patienter";                              // On commence sur le thread UI
       await Task.Run(OpérationEnArrièrePlan);              // La méthode OnButtonClick se termine à ce moment pendant que OpérationEnArrièrePlan se poursuit sur un autre thread.
       this.Text = "Fini";                                                // Une fois OpérationEnArrièrePlan terminée la fin de OnButtonClick est invoquée sur le thread UI.
    }

  5. #5
    Membre éclairé
    Inscrit en
    Septembre 2003
    Messages
    391
    Détails du profil
    Informations forums :
    Inscription : Septembre 2003
    Messages : 391
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    En gros tu remplaces la méthode "updateui" de "background" par un champ "updateui" de type Action qui sera fourni via le constructeur de "background".
    Je ne connais pas trés bien (je viens plutot du C++) mais j'ai essayé ceci :
    dans ma classe form :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
            private void button1_Click(object sender, EventArgs e)
            {   
                // prépare les element pour le thread
                worker.setup(par1.Text, par2.Text);
                worker.register(display);
    et dans la classe background :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
            private Action<string> updater;
            public void register(Action<string> dlg) // enregistre un delegate pour la maj de l'UI
            {
                updater = dlg;
            }
    et du coups je l'apelle ainsi (depuis la classe background) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
            private void updateui(string info)
            {
    //            BeginInvoke(new strParamDelegate(???.display), new object[]{info));
                updater(info);
            }
    Mais du coups, il y a un truc que j'ai pas bien fait, car j'ai une exception "invalidOperation", comme si j'avais appellé form.display() directement ! pourtant un "Action" est un delegate non ?


    Je ne suis pas contre un ptit coup de main supplementaire pour faire marcher ce code


    Sinon,
    Citation Envoyé par DonQuiche Voir le message
    Cela dit tu devrais te renseigner sur async/await
    Alors ca je ne connais pas du tout,
    c'est peut être parce que je cible du .NET 3.5 je viens de voir que c'est assez recent ce truc...

    Merci

  6. #6
    Membre Expert Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Par défaut
    Citation Envoyé par hpfx Voir le message
    Mais du coups, il y a un truc que j'ai pas bien fait, car j'ai une exception "invalidOperation", comme si j'avais appellé form.display() directement ! pourtant un "Action" est un delegate non ?

    Néanmoins, j'ai quand même une question : là on pert l'aspect asynchrone que l'on avait avec le BeginInvoke non ? bon, c'est pas bien grace en soit...

    Je ne suis pas contre un ptit coup de main supplementaire pour faire marcher ce code
    En fait c'est désormais ta méthode display qui doit s'occuper d'invoquer BeginInvoke. En somme celui qui instancie "background" prend aussi la responsabilité de gérer la synchronisation à la fin, via BeginInvoke, et encapsule tout cela dans le délégué fournit au constructeur de background.

    Donc ton code est bon à part ce petit détail. Côté propreté du code je te conseille de créer un "displayAsync" qui s'occupera d'invoquer "display" via BeginInvoke. Et tu passeras "displayAsync" à "background" au lieu de "display". Comme ça "displayAsync" ne s'occupe que de la plomberie de synchronisation et "display" uniquement de la plomberie UI.


    alors ca je ne connais pas du tout,
    c'est peut être parce que je cible du .NET 3.5 je viens de voir que c'est assez recent ce truc...
    En effet. Et c'est bien dommage car c'est vraiment formidable pour faire des aller-retours entre l'UI et d'autres threads. Ca évite beaucoup de plomberie comme tu as pu le voir.

Discussions similaires

  1. Réponses: 6
    Dernier message: 08/06/2015, 14h58
  2. Réponses: 4
    Dernier message: 23/05/2006, 14h22
  3. [Thread] Retrouver un thread dont on a plus la référence
    Par ptitjack dans le forum Concurrence et multi-thread
    Réponses: 4
    Dernier message: 01/10/2004, 10h56

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