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 Forms Discussion :

Exécution sous thread spécifique


Sujet :

Windows Forms

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 14
    Points : 5
    Points
    5
    Par défaut Exécution sous thread spécifique
    Bonjour à tous,

    Comment, depuis un thread B, exécuter une méthode sous un thread A ? Je m'explique.

    Le thread A est le thread principal, le main, le thread de base. Le thread B a été créé pour effectuer une tâche assez longue, en arrière plan et bien sûr pour éviter de bloquer le thread A. Une fois que le thread B a terminé son travail, j'aimerais que le thread B donne l'ordre au thread A d'exécuter une méthode quelconque, mais que cette méthode soit effectivement exécutée par le thread A et non par le B.

    Pourquoi ? La raison est que le thread A utilise une librairie XY qui n'accepte des instructions QUE du thread principal, donc le A. Si B appelle une méthode de cette librairie, l'instruction est refusée et génère une exception.

    En Windows Forms, il existe le magnifique BackgroundWorker qui fonctionne à merveille, sauf que mon application est en Console et doit le rester. Quelles sont les possibilités ?

    J'ai essayé notamment les appels asynchrones mais chaque appel asynchrone que j'ai pu faire dans une application console était exécuté dans un thread différent, ou encore le thread B.

    Merci d'avance pour vos réponses,

    Ritchouone

  2. #2
    Expert éminent
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Points : 8 344
    Points
    8 344
    Par défaut
    C'est peut être pas la solution la plus adaptée, mais j'ai pondu ça qui fonctionne chez moi :
    Code c# : 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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
     
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
     
    namespace ConsoleApplication1
    {
        class Program
        {        
            class ThreadRun
            {
                public delegate void ParameterLessMethod();
     
                protected virtual Queue<ParameterLessMethod> WorkQueue { get; set; }
     
                protected virtual int OriginalThreadId { get; set; }
     
                public ThreadRun()
                {
                    WorkQueue = new Queue<ParameterLessMethod>();
                    OriginalThreadId = Thread.CurrentThread.ManagedThreadId;
                }
     
                public virtual void Invoke(ParameterLessMethod method)
                {
                    if(method == null)
                        return;
     
                    if (Thread.CurrentThread.ManagedThreadId == OriginalThreadId)
                        method.Invoke();
                    else
                    {
                        lock (WorkQueue)
                        {
                            WorkQueue.Enqueue(method);
                        }
                    }
                }
     
                public virtual bool WorkPending
                {
                    get
                    {
                        lock (WorkQueue)
                        {
                            return WorkQueue.Count > 0;
                        }
                    }
                }
     
                public virtual void DoEvents()
                {
                    if (Thread.CurrentThread.ManagedThreadId != OriginalThreadId)
                        throw new InvalidOperationException("Cette méthode doit être appelée à partir du thread principal");
     
                    ParameterLessMethod currentMethod = null;
                    while (true)
                    {
                        lock (WorkQueue)
                        {
                            if (WorkQueue.Count < 1)
                                break;
                            else
                                currentMethod = WorkQueue.Dequeue();
                        }
     
                        Invoke(currentMethod);
                    }
                }
            }
     
            static int working = 0;
     
            static ThreadRun threadRun = new ThreadRun();
     
            static void Main(string[] args)
            {
                List<Thread> threads = new List<Thread>();
     
                for (int i = 0; i < 20; i++)
                {
                    working++;
     
                    Thread t = new Thread(new ThreadStart(ThreadMethod));
                    t.Start();
                    threads.Add(t);
                }
     
                while (working > 0 || threadRun.WorkPending)
                {
                    threadRun.DoEvents();
                }
     
                Console.ReadLine();
            }
     
            public static void ThreadMethod()
            {
                threadRun.Invoke(new ThreadRun.ParameterLessMethod(delegate
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                }));
     
                working--;
            }
        }
    }
    C'est juste l'idée du Application.DoEvents qui permet de faire fonctionner les Invoke et tout en WinForms.
    Après pour rendre le Invoke effectivement bloquant, c'est une autre histoire mais c'est envisageable.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 14
    Points : 5
    Points
    5
    Par défaut
    Merci pour le code mais je n'ai pas eu l'occasion de le tester.
    Visual Studio 2005 me donne 4 erreurs dont la suivante :
    Error 1 'ConsoleApplication1.Program.ThreadRun.WorkQueue.get' must declare a body because it is not marked abstract or extern C:\Documents and Settings\<user>\My Documents\Visual Studio 2005\Projects\Soluce1\Soluce1\Program.cs 15 70 Soluce1
    4 erreurs dont 2 pour le WorkQueue (set et get) et 2 sur OriginalThreadId (set et get).

    Une petite idée pour que je puisse tester ?

  4. #4
    Expert éminent
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Points : 8 344
    Points
    8 344
    Par défaut
    alala, c'est gens qui n'ont pas la joie du C# 3.0
    En C# 3.0 les champs correspondant aux propriétés peuvent être automatiquement crées. ça donne ceci :
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
               Queue<ParameterLessMethod> workQueue = null;
               int originalThreadId = 0;
     
                protected virtual Queue<ParameterLessMethod> WorkQueue
                {
                    get { return workQueue;}
                    set { workQueue = value;}
                }
     
                protected virtual int OriginalThreadId
                {
                    get { return originalThreadId ;}
                    set { originalThreadId = value;}
                }

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 14
    Points : 5
    Points
    5
    Par défaut
    Citation Envoyé par smyley Voir le message
    alala, c'est gens qui n'ont pas la joie du C# 3.0
    Ehhhhhh voui. La triste réalité des développeurs utilisant Mono qui n'est pas encore tout à fait au niveau du framework.

    Mais le problème n'est pas là, j'ai testé ton code, ça semble correspondre à ce que je veux, mais je ne suis pas sûr d'avoir totalement compris ton mécanisme. Je vais regarder ça de plus près d'ici demain (nan mais t'as vu l'heure aussi ?), mais si tu te sens la verve de me donner quelques explications à la volée, je suis toujours preneur.

    Merci encore pour ton aide,
    Meilleures salutations,

    Gitch

  6. #6
    Expert éminent
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Points : 8 344
    Points
    8 344
    Par défaut
    En fait c'est sur deux idées :
    - la première est que quand on prend un Delegate (quelqu'il soit), lorsque l'on appelle Invoke par défaut il est exécuté sur le thread appelant
    - je n'ai aucune idée de comment "transmettre" un code sur un autre thread, à part forcer le thread à exécuter ce code.

    Donc il suffit juste de créer une liste de delegates : les threads qui auront besoin de transmettre leurs appels au thread principal rajoute leurs appels dans la liste, et le thread principal lui la vide en bouclant sur la liste, jusqu'à ce que son action ne soit plus nécessaire ie. lorsque tous les threads ont fini leur travail et que le thread principal peux reprendre le cours de son exécution (la suite du programme). Et comme c'est le thread principal, tous les delegates rajoutés à la liste seront bien exécutés dans le thread principal.

    Mais dans mon code c'est pas vraiment un Invoke mais un BeginInvoke car l'appel est asyncrone. Si tu veux rendre le tout syncrone (ie. que les autres threads ne poursuivent pas leur tâche tant que l'appel sur le thread principal n'a pas été effectué) ça nécessitera de compléter un peux la classe pour arriver au résultat voulu, mais c'est pas infaisable.

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 14
    Points : 5
    Points
    5
    Par défaut
    Citation Envoyé par smyley Voir le message
    les threads qui auront besoin de transmettre leurs appels au thread principal rajoute leurs appels dans la liste, et le thread principal lui la vide en bouclant sur la liste, jusqu'à ce que son action ne soit plus nécessaire ie. lorsque tous les threads ont fini leur travail et que le thread principal peux reprendre le cours de son exécution
    Re!

    L'idée de la liste de Delegate est très bonne, elle permet effectivement que le thread principal exécute la totalité des tâches, mais c'est au niveau des attentes que ton idée ne correspond pas encore totalement sur ce que je souhaite.
    J'ai étudié ton code hier et j'ai fais la même constatation, le thread principal doit non seulement appeler lui-même la méthode DoEvents pour que la WorkQueue se vide mais il se "bloque" tant qu'il existe encore des Threads auxiliaires ou que toutes leurs tâches n'ont pas été effectuées.

    L'idéal serait que ça se déclenche automatiquement et sans bloquer le Main, mais avec ce concept, je ne vois pas comment le faire. Une dernière idée ?

    Merci d'avance

  8. #8
    Expert éminent
    Avatar de smyley
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    6 270
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 6 270
    Points : 8 344
    Points
    8 344
    Par défaut
    Citation Envoyé par ritchouone Voir le message
    L'idéal serait que ça se déclenche automatiquement et sans bloquer le Main, mais avec ce concept, je ne vois pas comment le faire. Une dernière idée ?
    Bah c'est difficile, vu que le thread ne peux pas exécuter un autre code s'il s'occupe déjà de quelque chose. Pendant que les autres threads tournent, il fait quoi ton thread principal ? (et d'ailleurs, ce qu'il fait ne pourrai pas être déplacé vers un autre thread ?)

Discussions similaires

  1. [WD5.5] Exécution sous XP
    Par HRAICHI dans le forum WinDev
    Réponses: 7
    Dernier message: 03/12/2010, 13h55
  2. Réponses: 3
    Dernier message: 16/03/2007, 11h36
  3. [FreePascal] Exécutables sous Windows et sous Linux
    Par etranger dans le forum Free Pascal
    Réponses: 8
    Dernier message: 24/09/2005, 19h58
  4. Exécutable sous JBuilder
    Par biglong dans le forum JBuilder
    Réponses: 4
    Dernier message: 29/05/2005, 15h39
  5. Erreurs d'exécution sous delphi 5
    Par nkd dans le forum Langage
    Réponses: 3
    Dernier message: 06/11/2004, 17h25

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