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 :

Quelle difference entre un Thread et un appel de méthode asynchrone (avec BeginInvoke) [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 Quelle difference entre un Thread et un appel de méthode asynchrone (avec BeginInvoke)
    Bonsoir a tous.
    Je connaissais les Threads et le BackGroundworker, j'apprends qu'il existe aussi les appels de méthodes asynchrone avec beginInvoke.
    Je ne voudrais pas trop vous obliger a trop rentrer dans les détails car je pense que le sujet est vaste, je me pose simplement ces 2 questions :
    - Est ce dans un thread secondaire que s’exécute le code démarré par BeginInvoke ? (On parle d'appel asynchrone et pas de Thread ce qui est un peu déroutant pour moi, quelle différence cela fait ?).
    - en pratique, j'envisage de rajouter une fonction d'enregistrement automatique a mon programme. Cette fonction devant s’exécuter en tache de fond sans bloquer l'interface ce qui m'amène a ces interrogations et également pour apprendre. Donc dans ce cas précis ou d'une façon général vaux t'il mieux privilégier l'un ou l'autre méthode ?

    Nombre d'entre vous ont un très bon niveau et ont du se poser ce genre de questions je pense, j'espère que vous saurez m’éclairer un peu dans ce genre de choix.
    Vos avis m'intéresse beaucoup. En vous remerciant par avance.

  2. #2
    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
    Boonjour.

    BeginInvoke est spécifique aux contrôles UI et le code passé en argument sera exécuté sur le thread UI. En effet les contrôles UI (WPF ou Winforms) utilisent un modèle dit "d'affinité de thread" : ils ne peuvent être manipulés que depuis le thread UI, ce qui est un mécanisme de synchronisation simple et sûr à gérer pour le programmeur. Ce thread UI possède une liste d'attente dans lequel les opérations à traiter sont enfilées. Tenter de modifier un contrôle UI depuis un autre thread lève une exception.

    Donc pour le cas d'un traitement de fond on démarrera celui-ci d'une autre façon (par exemple Task.Run ou via un BackgroundWorker) et lorsqu'il faudra déclencher une mise à jour de l'UI depuis ce thread on utilisera Invoke/BeginInvoke.

    Enfin, petit plus sympathique : si tu démarres une méthode asynchrone ("async en C#") sur le thread UI depuis laquelle tu attends une opération sur un autre thread (via "await" en C#), le thread UI continuera sa vie en effectuant d'autres traitements et une fois l'opération terminée la suite de ta fonction reprendra sur le thread UI le moment venu. Ce qui t'évite d'avoir à écrire des BeginInvoke et autres détails de la plomberie, comme si le compilateur avait fait ça de lui-même (en substance c'est le cas).



    PS : Désolé si le dernier paragraphe est confus mais je ne peux pas fournir d'exemple en VB.

    PPS : méthode et appel asynchrones sont deux choses différentes. Une méthode asynchrone est invoquée comme une méthode normale sauf que dès la première mise en attente (via "await" en C#), la fonction se termine et la suite du code sera appelée de façon asynchrone après l'attente.

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

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    Citation Envoyé par DonQuiche Voir le message
    BeginInvoke est spécifique aux contrôles UI
    partiellement faux

    begininvoke n'est pas lié aux controles, certes il existe une méthode begininvoke sur les controles mais ils en existent ailleurs

    il y a par exemple delegate.begininvoke

    begininvoke démarre bien un thread, et on lui passe généralement un callback, quand le thread a terminé ce qu'il devait faire il appelle le callback, et via endinvoke on récupère le résultat depuis ce callback (callback = méthode de rappel, donc ca peut etre un addressof)

    il y a plein de choses dans le genre avec des noms différents dans le framework
    sur les méthodes tel les sockets il y a beginconnect/endconnect (et avec send, receive et les autres)
    ca permet d'avoir des méthodes non bloquantes en plus des méthodes bloquantes
    (ou encore sur httpwebrequest il y a .begingetresponse)

    au final ca revient au même que des threads, la syntaxe est légèrement plus déroutante au départ


    quelques infos de plus :
    en wpf il y a une notion de dispatcher au dessus des threads, on peut donc demander à n'importe quel dispatcher d'invoker une méthode, ce qui permet de "passer" d'un thread à un autre
    en windows forms, seuls les controles ont la faculté de ramener sur leur thread via control.invoke / control/begininvoke ce qui permet d'éviter les cross threads
    il reste une possibilité en windows forms, pouvoir rappeler sur le thread appelant via la classe asyncoperation, c'est ce qu'utilise le backgroundworker
    il faut un begininvoke et pour les events il les fait sur le thread qui a appelé le runworkerasync, évitant les problèmes de cross thread car en général les bgw sont démarrés depuis le thread principal


    bref au final la notion derrière tout ca c'est qu'il faut qu'un thread de traitement puisse avertir un autre thread (souvent le thread d'ui)
    le bgw rempli très bien ce rôle

    il y a aussi d'autres méthodes comme un thread qui remplie des données mais qui n'averti personne, ceux sont les autres classes qui viennent lire des infos de rafrachissement (via un timer pour une form par exemple)
    par contre après ca nécessite parfois d'apprendre les notions de verrous qui sont encore plus complexes (si un thread rempli une collection, il ne faut pas qu'un autre lise la collection)
    (si le thread averti ceux qui ont besoin des infos, la collection est prete et il n'y a pas ces problématiques)
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  4. #4
    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 Pol63 Voir le message
    partiellement faux
    Sauf que les cas que tu cites relèvent d'autre chose : Delegate.BeginInvoke par exemple ce n'est pas un mécanisme d'affinité de thread et il est très improbable que ce soit l'objet de la question posée. On ne peut pas parler en général de tout ce qui pourrait avoir été appelé "BeginInvoke" car les réponses diffèrent.

    begininvoke démarre bien un thread
    Attention à la formulation : à propos de delegate.BeginInvoke on passe par un ThreadPool donc on va peut-être créer un thread ou en réveiller un mais peut-être celui-ci était-il déjà créé et actif.

    Et ce n'est pas du tout le cas concernant le BeginInvoke de WinForms/WPF, je ne sais pas s'il était très clair pour le lecteur que ces paragraphes-là se concentraient sur Delegate.BeginInvoke.


    il reste une possibilité en windows forms, pouvoir rappeler sur le thread appelant via la classe asyncoperation, c'est ce qu'utilise le backgroundworker
    En fait ce n'est pas spécifique à WindowsForms : le mécanisme est disponible pour toutes les occurrences de SynchronizationContext, y compris WPF, Silverlight et Windows Store. C'est aussi ce qui est mis en oeuvre par async/awiat pour reprendre l'exécution sur le thread UI d'origine, que ce soit en WPF, WinForms ou d'autres.

    Au passage, je pense vraiment que la classe BackgroundWorker n'a plus de raison d'être avec les méthode asynchrones (async/await) : celles-ci permettent de ne pas avoir à écrire de plomberie et de ne pas éclater le code du traitement sur plusieurs classes, on garde ainsi un code lisible.

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

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    oui, j'ai du écrire mon lot d'approximation aussi, ce n'est pas un sujet simple qui peut se résumer en quelques lignes ^^
    on a pas énormément explorer ce sujet, on préfère s’embêter avec des verrous car nos threads font ce qu'ils veulent (un thread rempli, un autre lit ...)


    Citation Envoyé par DonQuiche Voir le message
    Au passage, je pense vraiment que la classe BackgroundWorker n'a plus de raison d'être avec les méthode asynchrones (async/await) : celles-ci permettent de ne pas avoir à écrire de plomberie et de ne pas éclater le code du traitement sur plusieurs classes, on garde ainsi un code lisible.
    ca je suis bien d'accord
    pas encore testé ce qu'il y a sur le fx 4.5, mais déjà avant on peut écrire des choses presque lisibles avec les méthodes anonymes, en ayant accès à toutes les variables, ce qui évite de fractionner et de faire transiter plein de paramètres
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    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 Pol63 Voir le message
    pas encore testé ce qu'il y a sur le fx 4.5
    Laisse-moi te mettre l'eau à la bouche alors.

    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
    public async void OnButtonClick(...)
    {
       AssertThreadIsUI()
       var resultat = await Task.Run(UneLongueOperation);
     
       // Durant l'attente OnButtonClick se termine et le thread UI continue sa vie. La partie ci-dessous sera exécutée plus tard.
     
       AssertThreadIsUI()
       MettreAJourUI(resultat)
    }
     
    object UneLongueOperation()
    {
       AssertThreadIsBackground()
       ...
    }

    Les "Assert" sont évidemment optionnels et uniquement destinés à illustrer le code. Ces assertions sont toujours vérifiées.

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

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 154
    Points : 25 072
    Points
    25 072
    Par défaut
    intéressant, faudra que je me penche rapidement là dessus
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  8. #8
    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,
    Merci beaucoup pour vos réponses très intéressantes mais j'ai eu du mal a vous suivre .
    j'ai peut-être compris certaines choses (j'espère vous rectifierait si je dit des bêtises) :
    - Un Thread ne peux pas exécuter du code qui va modifier des contrôles si ces derniers n'ont pas été crées (instanciés) a l’intérieur de ce Thread. Dans ce cas on utilise MonControl.Invoke/BeginInvoke qui "ramène" l’exécution du code sur le Thread qui a crée le contrôle. (En principe il s'agit du Thread du programme principal car c'est depuis celui ci que sont crées les contrôles en règle général).
    - l'exemple précédent invoquait directement du code a partir d'un contrôle (j'espère que j'utilise le bon vocabulaire), on a aussi la possibilité d'invoquer du code a partir d'un délégué pointant vers une méthode a exécuter. (Mes questions se rapportant davantage a cette exemple précis). Donc lancer une méthode avec un Deleguate.BeginInvoke revient finalement a peu de chose prés a faire la même chose que lancer un thread. On aura au final du code qui s’exécute en parallèle dans le thread appelant et dans la méthode 'invoquée'. Cette dernière s’exécutant soit dans un nouveau Thread ou 'réveille' un Thread déjà existant. Quand vous dite réveille, est-ce que c'est l'objet Deleguate qui sert de 'lien' avec le Thread pour savoir quel Thread le programme doit réveiller ?

    Est ce que les arguments que vous avancé en faveur de (async/await) s'applique aux Deleguate ? (Comme ce que j'ai noté : la possibilité de ne pas avoir a éclater le code sur plusieurs classes ou encore éviter d'avoir à écrire des BeginInvoke et autres détails de la plomberie, comme si le compilateur avait faisait ça de lui-même).

    Le sujet est très vaste en effet et j'espère que je ne mélange pas tout mais pour le moment j'ai peur de manquer d'arguments pour m'aider a faire un choix entre les deux possibilités. A priori les deux possibilités reviennent a faire la même chose lorsque qu'il s'agit simplement d’exécuter du code en parallèle. Un ou deux éléments de réponses me permettrait sans doute de mieux comprendre pour m'aider a faire mes propres choix, en tout cas j'espère que ça viendra avec le temps, pour autant que le temps ai quelque chose a voir la dedans .

  9. #9
    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
    Tu as bien tout compris.

    Concernant Delegate.BeginInvoke, le délégué est en fait mis en file sur ThreadPool : il s'agit d'un bassin de threads statique, unique à l'application. Ce bassin est utilisé par Delegate.BeginInvoke, Task.Run, le BackgroundWorker et on peut en plus l'utiliser manuellement via ThreadPool.QueueUserWorkItem. En substance il sert chaque fois qu'on a besoin d'un thread quelconque pour un traitement en arrière-plan. Note aussi qu'il a vocation à être partagé : le dotnet framework peut l'utiliser pour certaines opérations, tout comme les bibliothèques que tu utilises dans ton application, etc.

    Ce ThreadPool gère une file d'attente des travaux à effectuer et s'occupe de créer automatiquement des threads, chacun constitué d'une boucle qui va puiser de nouvelles opérations dans la file et se mettre en sommeil quand il n'y a rien à faire. Quand une nouvelle opération est ajoutée, le bassin réveille tous les threads endormis et tous se ruent sur la file d'attente pour prendre une opération. S'il n'y en n'a pas assez pour tout le monde, les derniers se rendorment.

    Note que le nombre de threads sera ajusté dynamiquement pour maximiser les performances : on mesure l'efficacité avec N threads, on en ajoute un, on mesure et on utilise une interpolation pour ajuste le nombre de threads. Cette formule empirique ne fonctionne vraiment bien qu'avec des opérations suffisamment nombreuses et pas trop courtes mais le reste du temps, même si c'est suboptimal, ce n'est généralement pas un problème.


    Maintenant en vrac :
    * Un délégué n'emporte aucune information sur le thread sur lequel il est exécuté et il n'a pas à faire de choix de thread. Un délégué ce n'est qu'une adresse pointant vers le code à exécuter (et parfois, en plus, une instance dont les membres stockent certaines valeurs, comme avec les fermetures/clôtures/closures).

    * Await ne peut pas être utilisé avec Delegate.BeginInvoke car celui-ci ne renvoie pas de tâche (Task). Or il faut une tâche à attendre pour le mot-clé await. Mais Task.Run fait exactement la même chose que Delegate.BeginInvoke et renvoie en plus une tâche.


    Enfin il y a beaucoup de redondance car le framework dotnet a évolué : le parallélisme de tâches (Task) n'a été ajouté que récemment et les mots-clés async/await encore plus récemment. Je dirais qu'ils supplantent tout ce qui est Delegate.BeginInvoke, le BackgroundWorker, et même souvent Control.BeginInvoke et car ils ciblent exactement les mêmes scénarios, et davantage, si bien que ces derniers ne devraient plus être utilisés (pas toujours possible pour Control.BeginInvoke).

    Groso modo, si tu n'as pas besoin d'un pilotage très fin du parallélisme, utilise Task.Run pour lancer tes threads. Si tes besoins de synchronisation des threads sont simples, tels qu'une action tour à tour (quand X fini, faire Y) utilise async/await, et s'ils sont un poil plus complexe alors crée manuellement des chaînes avec Task.ContinueWith. Si tes besoins de synchronisation des données sont standards, regarde les collections concurrentes (ConcurrentQueue, etc). Enfin si ton algorithme est du type boucle (traiter N éléments en parallèle) ou autres motifs commun, la classe Parallel automatise ça en s'appuyant sur les tâches, via des méthodes comme Parallel.Foreach

    Et si jamais il te fallait des choses plus sophistiquées, tourne-toi vers les primitives : création manuelle de threads, opérations atomiques avec Interlocked, primitives de synchronisation comme Semaphore, Mutex, EventWaitHandle ou SpinLock, etc. Mais là c'est beaucoup plus fastidieux, complexe et dangereux.

  10. #10
    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 beaucoup pour la réponse.
    Encore une fois je n'ai pas tout compris mais l'essentiel a été dit. Ca va beaucoup m'aider a y voir plus clair.
    Il y a des mots que je connaissais même pas dans les deux derniers paragraphe, j'ai l'impression de m'être fait avoir avec mes livres de 500 pages (Humour ).
    Je passe en résolu, merci beaucoup.

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

Discussions similaires

  1. Quelles Differences entre UML et Merise?
    Par hugobob dans le forum Méthodes
    Réponses: 6
    Dernier message: 13/03/2013, 12h05
  2. Quelles differences entre Core Duo et Core 2 Duo?
    Par The-Most-Wanted dans le forum Composants
    Réponses: 4
    Dernier message: 22/05/2008, 13h12
  3. Quelle difference entre & et && ?
    Par sayag dans le forum C#
    Réponses: 2
    Dernier message: 24/06/2007, 10h18
  4. Réponses: 3
    Dernier message: 02/04/2006, 19h38
  5. Réponses: 3
    Dernier message: 16/01/2006, 10h29

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