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

Framework .NET Discussion :

[C#][2.0] Sauvegarde d'un DataSet dans des threads différents


Sujet :

Framework .NET

  1. #1
    Membre actif
    Inscrit en
    Août 2006
    Messages
    381
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 381
    Points : 252
    Points
    252
    Par défaut [C#][2.0] Sauvegarde d'un DataSet dans des threads différents
    Bonjour,

    je suis confronté à un problème assez délicat:
    La sauvegarde d'un DataSet dans des threads différents.

    Ce code est exécuté sur un serveur. Le client identifie le bidon à l'entrée du convoyeur et appelle la méthode EntreeBidon.
    Le bidon est ajouté à une file d'attente puis le statut est sauvegardé dans la base de données via .Net Remoting car cette base de données est géré par un serveur distant lui aussi.

    Lorsque le bidon sort du convoyeur la méthode SortieBidon est appelé.
    J'ai quelques soucis comme je le disais pour la gestion des threads. En effet, la sauvegarde dans la base de données est susceptibles de prendre un peu de temps. J'utilise donc le pool de threads pour la sauvegarde.
    Pour ne pas avoir de problème d'accès concurrents, j'ai utilisé un lock pour verrouiller le DataSet à sauvegarder, mais je ne trouve pas ça terrible.

    Comment devrais-je faire selon vous ?

    Voilà comment j'ai procédé

    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
     
    private Queue<ProduitDataSet.BidonRow> m_FileBidons;
     
    void EntreeBidon(ProduitDataSet dsProduits, int idBidon)
    {
          ProduitDataSet.BidonRow bidon = dsProduits.Bidons.FindByBidonId(idBidon);
     
          if (bidon!=null)
          {           
               m_FileBidons.Enqueue(bidon);
     
    	   bidon.Statut = "EnCours";
     
               // Sauvegarde du DataSet des produits
               ThreadPool.QueueUserWorkItem(new WaitCallback(SaveProduitDataSet), dsProduits);
     
          }
    }
     
     
            /// <summary>
            /// Sauvegarde du DataSet
            /// </summary>
            private void SaveProduitDataSet(object objProduitDataSet)
            {
                ProduitDataSet dsProduit = objProduitDataSet as ProduitDataSet;
     
                lock (m_ProduitDataSet)
                {
                    // Fusion des bidons modifiés
                    if (dsProduit.Bidons.GetChanges(DataRowState.Modified) != null)
                    {
                        m_ProduitDataSet.Bidons.Merge(dsProduit.Bidons.GetChanges(DataRowState.Modified), false);
                    }
     
                    // Sauvegarde du DataSet
                    try
                    {
                        // Test Endormissement du thread courant
                        Console.WriteLine("Endormissement du Thread. {0}", Thread.CurrentThread.ManagedThreadId);
                        System.Threading.Thread.Sleep(10000);
                        Console.WriteLine("Réveil du Thread. {0}", Thread.CurrentThread.ManagedThreadId);
     
     
                        // Sauvegarde du DataSet
                        // m_GestionProduit est une interface distante accessible via .Net Remoting
                        m_GestionProduit.SaveProduits(m_ProduitDataSet);
     
     
                        // Valide les changements apportés
                        m_ProduitDataSet.AcceptChanges();
     
                    }
     
                    catch (Exception ex)
                    {
                        log.Error("Erreur lors de la sauvegarde du DataSet des produits.", ex);
                    }
                }
            }
     
     
            private void SortieBidon()
            {
                 ProduitDataSet.BidonRow bidon = dsProduits.Bidons.FindByBidonId(idBidon);
     
                 bidon.Statut = "Termine";
     
                 SaveBidon(bidon);    
     
            }
     
            /// <summary>
            /// Sauvegarde des modifications apportées à la ligne d'un bidon
            /// </summary>
            private void SaveBidon(ProduitDataSet.BidonRow rowBidon)
            {
                if (rowBidon!= null)
                {
                    // Transtypage de la table du carton
                    ProduitDataSet.BidonsDataTable dtBidons = rowBidon.Table as ProduitDataSet.BidonsDataTable;
     
                    try
                    {
                        // Sauvegarde des modifications du cartons
                        m_GestionProduit.SaveBidon(dtCartons, rowBidon.BidonId);
     
                        rowBidon.AcceptChanges();
                    }
                    catch (Exception ex)
                    {
                        log.Error("Erreur lors de la sauvegarde du bidon.", ex);
                    }
                }
            }

    Je suis désolé si ce message est un peu long mais j'y suis obligé.
    En espérant avoir été clair.

    Merci d'avance pour votre aide.
    A bientôt

  2. #2
    Membre expérimenté Avatar de Mose
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 143
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 143
    Points : 1 379
    Points
    1 379
    Par défaut
    Suggestion :
    * tu encapsules ton DataSet dans un objet métier.
    * tu y colles un booleén 'busy' que tu mets à vrai quand tu lance ta sauvegarde
    * si qqn essayes d'accéder au DataSet (via les méthodes de ton objet métier), tu vérifies qu'il n'est pas "busy". S'il l'est, tu vois ce que tu fais (annuler la sauvegarde, ou faire patienter la modification)

    Maintenant, faut voir si ça colle avec ton archi...

  3. #3
    Membre actif
    Inscrit en
    Août 2006
    Messages
    381
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 381
    Points : 252
    Points
    252
    Par défaut
    Bonsoir,

    pas bête l'idée du busy. Je vais voir.
    Et tu as eu le temps de jeter un coup d'oeil à mon code ? Qu'en penses-tu ? Oui oui je sais y en a des tartines. Tu confirmes que les lock, ce n'est pas terrible ?

    Merci d'avance.
    Bye

  4. #4
    Membre expérimenté Avatar de Mose
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 143
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 143
    Points : 1 379
    Points
    1 379
    Par défaut
    Bein... les lock avec le gros sleep(10000) dedans, effectivement je trouve pas ça terrible

    Perso j'évite les lock le plus possible, généralement, l'utilisation d'un booléan permet d'éviter bien des soucis à un coût très minime en perfs.

    Une autre solution : tu fais un lock, tu clones ton DataSet, tu sors du lock et tu fait ta sauvegarde avec le clone, comme ça l'autre peut être modifié sans impacts.

  5. #5
    Membre actif
    Inscrit en
    Août 2006
    Messages
    381
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 381
    Points : 252
    Points
    252
    Par défaut
    Bonsoir,

    Bein... les lock avec le gros sleep(10000) dedans, effectivement je trouve pas ça terrible
    Oui tu m'étonnes. J'ai utilisé un sleep pour simuler le blocage de l'appli. Evidemment en prod, ce sleep n'y ait pas.

    Mon archi est la suivante:
    Une application serveur de gestion gérant les accès à la base de données.
    Une application serveur de suivi de produit.
    Le serveur de suivi de produit est client du serveur de Gestion (.Net Remoting).
    Au total, le serveur de gestion a 8 clients et le serveur de traitement a 4 clients.

    Si je mets un booleen busy comme tu me l'as suggéré, les sauvegardes du DataSet ne seront pas possible tant que ce booleen n'est pas à false.
    Le serveur de traitement sauvegarde très régulièrement en base le DataSet.
    Il risque donc d'occuper très souvent l'opération de sauvegarde du DataSet sur le serveur de Gestion et les autres clients ne pourront pas sauvegarder comme ils le souhaitent. Non ? Qu'en penses-tu ?

    L'autre solution n'est pas mal non plus.

    Je te remercie pour tes conseils.
    Bye

  6. #6
    Membre actif
    Inscrit en
    Août 2006
    Messages
    381
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 381
    Points : 252
    Points
    252
    Par défaut
    Bonjour,

    je galère toujours avec ce problème.
    En fait, j'ai un contrôle utilisateur et une classe métier.
    L'utilisateur scan un code-barre avec une douchette. Celui-ci est transmis sous forme de chaîne de caractère à la classe métier.
    Celle-ci effectue un traitement et sauvegarde les données dans un DataSet qui est ensuite sauvegardé dans la base de données à chaque scan.
    Je devrais peut-être faire de ma classe métier une classe thread-safe ?
    Non ? Qu'en pensez-vous ?
    Si j'utilise un booleen, il faut que je m'assure aussi qu'il n'est pas modifié par plusieurs thread différent.

    Merci pour votre aide.

  7. #7
    Membre expérimenté Avatar de Mose
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 143
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 143
    Points : 1 379
    Points
    1 379
    Par défaut
    Pour le booléen : tu peux utiliser un mutex au moment où tu veux le modifier.
    Je pense pas que le lock fonctionne sur un ValueType, mais ça vaudrait le coup d'essayer...

    Avec ça et le contrôle de toute écriture/lecture sur le booléan, ton DataSet sera Thread-Safe

  8. #8
    Membre actif
    Inscrit en
    Août 2006
    Messages
    381
    Détails du profil
    Informations forums :
    Inscription : Août 2006
    Messages : 381
    Points : 252
    Points
    252
    Par défaut
    Bonjour,

    en fait je me suis aperçu que mon problème était un peu plus vicieux.

    Le bidon est scanné puis doit être sauvegardé dans la base de données.
    La méthode AddBidons est appelée dans des threads différents à chaque scan.

    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
     
    public class BusinessBidon
    {
          private ProduitDataSet dsProduits;
          private BackgroundWorker wker;
     
          internal void AddBidons(Bidon bdn)
          {
               dsProduits.Bidon.Add(bdn.reference, bdn.numero, bdn.OF);
     
               wker.RunWorkerAsync();          
          }
     
          private bool SaveBidons()
          {
                try
                {
                      wsTraitementBidon.SaveProduits(dsProduits);                  
                }
                catch (Exception ex)
                {
                      log.Error("Erreur de sauvegarde", ex);
                      return false;
                }
     
                return true;
          }
     
          private void wker_DoWork(object sender, DoWorkEventArgs e)
          {
             e.Result = SaveBidons();
          }
     
          private void wker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
          {
              bool result = (bool)e.Result;
     
              if (result)
              {
                  log.Debug("Validation des changements.");
     
                   // Validation des changements
                  m_OTDataSetCertification.AcceptChanges();
              }
          }
    }

    Donc le problème qui se pose est le suivant:
    Le bidon est ajouté puis sauvegardé. Si la sauvegarde réussit, les changements du DataSet sont validés par appel de la méthode AcceptChanges.
    Si un bidon est ajouté entre la sauvegarde et la validation du DataSet, le bidon ajouté ne sera jamais sauvegardé car même s'il est bien dans le DataSet, la ligne ne sera plus marqué comme ADDED et donc ne sera jamais sauvegardé en base.

    Je désespère.
    Merci de votre aide.
    Bye

  9. #9
    Membre expérimenté Avatar de Mose
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 143
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 143
    Points : 1 379
    Points
    1 379
    Par défaut
    Reste une autre méthode :
    * au moment de "sauver", tu verrouilles ton DS
    * tu le clones
    * tu le dévérouilles
    * tu envoies ce clone dans une file d'attente.

    * Et parallèllement tu as un thread qui va faire les sauvegardes au fur et à mesure en vidant la file d'attente.

  10. #10
    Rédacteur
    Avatar de abelman
    Inscrit en
    Février 2003
    Messages
    1 106
    Détails du profil
    Informations forums :
    Inscription : Février 2003
    Messages : 1 106
    Points : 2 629
    Points
    2 629
    Par défaut
    Si un bidon est ajouté entre la sauvegarde et la validation du DataSet
    C'est justement ça qu'il faut eviter je pense.
    Pour cela le plus simple serait que l'ajout du bidong, la sauvegarde et la validation se fasse dans une même fonction, et ses trois opérations devront être protégé par le même lock.

Discussions similaires

  1. Réponses: 3
    Dernier message: 12/04/2011, 15h16
  2. Réponses: 2
    Dernier message: 30/08/2007, 08h28
  3. Réponses: 1
    Dernier message: 27/06/2006, 13h09
  4. [VB.NET] Création MDIChild dans un thread différent
    Par XnoTonio dans le forum Windows Forms
    Réponses: 5
    Dernier message: 19/05/2006, 15h53
  5. Comparaison de 2 dates dans des formats différents
    Par frdek dans le forum Langage SQL
    Réponses: 2
    Dernier message: 14/02/2005, 15h05

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