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 :

Quel pattern ou type de thread me conseilleriez vous ?


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut Quel pattern ou type de thread me conseilleriez vous ?
    Bonjour à vous.

    Je commence à avoir l'habitude d'utiliser les backgroundworker, lock et invoke, et d'ailleur je me limite quasiment qu'à ça. Car à chaque fois les threads = plein de problèmes

    Ahh les lock... je les utilisent que si nécéssaire, sur un minimum de ligne de code dont je controle crois controler la durée de vie. J'utilise un minimum de variable de lock, et pourtant malgrès tout des fois ça bloque.

    Bref ... les threads et moi ne faisont pas très bon ménage.

    Alors :
    Aujourd'hui dans mon programme aux millions de lignes de code (j'exagère à peine), je veux placer une methode dans un thread, cette methode est appellée une fois pour initialiser pas mal de choses.

    Et je n'ose pas le faire. Car je sens que des variables vont se paumer d'un thread à l'autre, qu'un event va appeller le control d'une form, bref j'ai peur !

    d'où la question : quel patern ou type de thread me conseilleriez vous? idéalement que ça ait un impact minimum sur mon code???

    Désolé, je me sent mauvais en écrivant tout cela... j'utilise le Framework 3.5

  2. #2
    Membre chevronné
    Profil pro
    Inscrit en
    Juin 2002
    Messages
    332
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Juin 2002
    Messages : 332
    Par défaut
    Personnellement, je refuse tous les contrats où j'aurais à développer en 3.5. Passe à 4.5 avec C# 5 et utilise la nouvelle approche multi-thread qui a justement été conçue pour minimiser les maux de tête.

  3. #3
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    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 204
    Par défaut
    Citation Envoyé par Babyneedle Voir le message
    Personnellement, je refuse tous les contrats où j'aurais à développer en 3.5. Passe à 4.5
    super !
    le conseil inutile de la journée
    tout le monde ne peut pas choisir ses outils de développements
    et puis le framework 3.5 contient déjà tout ce qu'il faut pour faire du multithreading, les apports du 4.5 permettent de gagner un peu de temps en codage mais nécessitent quand même de comprendre le fonctionnement et les locks

    @giova_fr
    un programme mal fait a souvent trop de lignes de code et des enchevêtrements de code qui le rende buggable à la moindre modif
    concernant le multithreading, un variable n'appartient pas à un thread, donc elle ne peux pas se perde, c'est régit par les lois de la portée comme toutes les variables
    donc déjà écrire son code en bonne POO cloisonnée ca aide à éviter les problèmes

    sur les patterns de multithreading il faut comprendre quelles sont les problèmes potentiels et les moyens de les contourner
    - les controles (et les dependencyObject en wpf) ne sont modifiables que depuis le thread les ayant instancié
    il convient donc de demander à ce thread d'appeler le code qu'on souhaite pour faire les modifs (delegate, events du backgroundworker etc...)
    - pour une variable simple sans verrouillage il y a risque de perte de données
    quand on incrémente un entier par exemple il y a une lecture de la valeur, une augmentation de 1 puis un restockage de la valeur
    si 2 threads demandent une incrémentation on risque de perdre une des deux incrémentations, pour ca il y a les incrémentations atomiques
    pour une variable objet, si on instancie l'objet dans une variable publique, puis qu'on remplie ses propriétés, et qu'un autre thread utilise cette variable il risque de l'utiliser avant qu'elle soit finie d'être inialisée, pour ca on peut remplir une variable locale privée puis la placer dans la variable publique à la fin de l'init, l'autre thread a juste à tester si la variable est à null pour attendre avant de l'utiliser
    - pour les collections si un thread fait un for each et qu'un autre thread fait un .add ca plante, pour ca il faut verrouiller

    il y a plusieurs types de verrouillages, le lock, on lock une variable (généralement un new object public), un autre thread demandant le lock de cette variable attendra que le 1er sorte du lock pour continuer
    ca permet donc pour un traitement d'être sur de ce qui se passe
    dans certains cas pour plus de performances il y a le readerwriterlock(|slim)
    il peut y avoir plusieurs lecteurs en même temps (accès à une collection par exemple) mais en cas d'écriture tout le monde attends (.add par exemple)

    exemple d'une propriété singleton en lazy initialization:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private static object _verrouPourMachin = new object();
     
    private static truc _machin; 
    public static truc machin {
         get {
             if (_machin != null)             return _machin;
             lock (_verrouPourMachin) {
                 if (_machin != null)                 return _machin;
                 _machin = new truc();
                 //initialisation
             }
         }
     }
    le lock fait que potentiellement plusieurs threads sont en attente sur cette ligne, donc juste après on retestes si pas null on return, pour ne pas instancier plusieurs fois et ce retrouver avec des variables perdues et qui ne sont plus uniques du coup

    après il y a d'autres choses, mais c'est selon les besoins, si tu nous en dis plus sur tes problèmes on pourra t'aiguiller vers ce qu'il faut faire ...
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  4. #4
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut
    Merci Pol pour ta réponse détaillée.
    Passe à 4.5
    C'est une appli grand public, et certains utilisateurs sont sous vista (malheureusement). Déjà qu'ils ont la flemme de lire le guide de démarrage qui fait une demie page, alors leur demander de faire un windows update, il ne faut pas compter dessus

    Pol : Réactions à ton post
    concernant ton exemple, à quoi sert la premiere ligne hors du lock?
    pourquoi ne pas mettre directement le lock?

    Ce que je fais en général va je crois dans le même sens que ce que tu as ecris :
    pour les collections, j'utilise readerwriterlockslim.

    Sinon je m'arrange pour que seul un des 2 threads ecrive dans une variable, et je fais systématiquement ceci pour "partager" une variable (continuer dans cet exemple):

    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
     
    bool continuer;
    readonly object _lock = new object();
     
    void ThreadA()
    {
        continuer = true;
        //Demarre threadB();
        Thread.Sleep(1000);
        lock(_lock)
            continuer = false;
    }
     
       void ThreadB()
       {
            bool localcontinuer = true;
            while(localcontinuer)
            {
                 //Un peu de boulot
                 lock(_lock)
                     localcontinuer = continuer;
            }
        }
    J'essaye d'avoir un minimum d'objets lock pour éviter les interlock
    je fais des lock ultra court, sans appel à des methodes dont je ne maitrise pas la durée de vie

    Et malgrès tout, des fois ça se fige

    Mes Besoins

    Mes besoins sont on ne peut plus classiques, par exemple j'ai une methode qui dure et je ne veux pas figer l'IHM, j'ouvre une fenetre "veuillez patienter" avec une animation qui rassure l'utilisateur, puis je lance mon thread.

    Un des problèmes c'est qu'on est souvant obligé de passer par une callback. Je déteste les callback/events de thread !
    Comment faire pour que cette callback ne prolonge pas la durée de vie du 2eme thread?
    Comment faire quand on ne veut pas de callback? :
    on veut que la methode appellant le 2eme thread (ThreadA dans mon exemple) soit bloquante sans pour autant figer l'IHM ?

    Je me retrouve à faire des whiles dégueulasses:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    while(cabosse && TimeoutDeSecu)
    {
    Thread.Sleep(40);
    Application.DoEvents();
      lock(_lock)
        cabosse= infoDeThreadB;
    }
    if(cabosse)
       throw new TimeOutExcection("blablabla");
    Quand je prévois de faire du multithread dès le début de la conception, ca va.
    Mais quand je veux 6 mois plus tard placer une methode dans un thread, je me retrouve à chercher une solution "anti-callback"
    Car tout le reste du code, s'attend à une methode bloquante, et je ne veux pas qu'une callback crée une fuite de thread (que j'appelle "sommeil éternel du thread principal")

  5. #5
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    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 204
    Par défaut
    Citation Envoyé par giova_fr Voir le message
    concernant ton exemple, à quoi sert la premiere ligne hors du lock?
    pourquoi ne pas mettre directement le lock?
    si tu parles de la ligne avant le lock, c'est parce qu'une fois la variable créé il ne faut bloquer personne pour vérifier son existence, si elle existe on la retourne

    Citation Envoyé par giova_fr Voir le message
    Un des problèmes c'est qu'on est souvant obligé de passer par une callback. Je déteste les callback/events de thread !
    Comment faire pour que cette callback ne prolonge pas la durée de vie du 2eme thread?
    Comment faire quand on ne veut pas de callback? :
    on veut que la methode appellant le 2eme thread (ThreadA dans mon exemple) soit bloquante sans pour autant figer l'IHM ?
    le principe normal c'est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    button.click
      démarrer thread
      showdialog patientez
     
    voidthread
      traitement
      invoke fin traitement
     
    fin traitement
        fermer fenetre showdialog
    moi les méthodes avec callback du framework que je connais elles sont des méthodes synchrones aussi
    dans ce cas dans le thread on appelle les méthodes synchrones
    si tu en as trouvés sans méthodes synchrones je veux bien savoir lesquelles
    enfin dans ce cas il suffit de modifier le schéma de la sorte je pense

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    button.click
      démarrer thread
      showdialog patientez
     
    voidthread
      traitement
      mthAsync(callback)
     
    callback
      suite traitement si nécessaire
      invoke fin traitement
     
    fin traitement
      fermer fenetre showdialog
    un callback étant appelé sur le thread qui l'a appelé

    Citation Envoyé par giova_fr Voir le message
    Je me retrouve à faire des whiles dégueulasses
    en effet ^^
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  6. #6
    Membre éclairé
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Juin 2005
    Messages
    700
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Juin 2005
    Messages : 700
    Par défaut
    Encore merci, c'est vraiment sympa de ta part !

    Qu'entends tu par mthAsync?

    Et les invoke, ok c'est super, mais invoke c'est dans une form
    Ca veut dire qu'une fenetre en plus du role d'afficher et interagir avec l'utilisateur, doit rapatrier l'execution dans le thread principale. Pas génial non?

    Enfin des fois mon histoire de thread se passe dans une assembly n'ayant pas de référence vers windows form. Ou bien une classe qui n'a aucun rapport avec une form, pourtant ses methodes sont appellée dans le thread principale (et du coup figent l'HIM). Exemple improvisé :

    [Monprogramme.exe]
    KeyboardListener.SpaceBarPressed =>
    [MaGestionMultimedia.dll]
    MonGestionnaire.StartPlaySansThread =>
    [LibrairieTiers.dll]
    TiersMP3Player.Play
    Tout cela se passe totalement hors du contexte d'une Form, et SpaceBarPressed s'est executé dans le thread principale.Donc l'HIM est figé jusqu'à ce que Play retourne.

    Je veux donc déplacer une partie de l'execution dans un thread :

    [Monprogramme.exe]
    KeyboardListener.SpaceBarPressed =>
    [MaGestionMultimedia.dll]
    MonGestionnaire.StartThreadPlay =>
    [LibrairieTiers.dll]
    Play =>callbackPlayed
    comment faire un invoke? KeyboardListener , MonGestionnaire, et la librairieTiers ne connaissent pas de Form. Ca n'est pas leur role...

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

Discussions similaires

  1. [Conception] quel est le type de variable a choisir?
    Par King_T dans le forum PHP & Base de données
    Réponses: 5
    Dernier message: 29/05/2006, 01h27
  2. [conception] quel pattern choisir?
    Par r0d dans le forum C++
    Réponses: 4
    Dernier message: 26/04/2006, 19h56
  3. Quel est le type retourné ?
    Par Rupella dans le forum C
    Réponses: 4
    Dernier message: 30/11/2005, 15h01
  4. [langage] "@$" Quel est ce type de variable?
    Par YanK dans le forum Langage
    Réponses: 4
    Dernier message: 21/04/2005, 19h07
  5. Réponses: 1
    Dernier message: 26/03/2004, 15h14

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