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

Access Discussion :

Plantage moteur JET


Sujet :

Access

  1. #1
    Membre régulier
    Inscrit en
    Juillet 2006
    Messages
    75
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 75
    Points : 76
    Points
    76
    Par défaut Plantage moteur JET
    Bonjour,

    Bon je ne sais pas trop si ce sujet a sa place ici ou dans la section C++... tant pis je me lance !

    Je suis en train de développer un soft de traitement d'images avec visual C++ 6.0. Ce soft enregistre ses résultats dans une base de donnée Access 97 ( 1 à 10 enregistrements par seconde ).
    La base de donnée était pilotée dans un premier temps par les drivers ODBC. Au bout de quelques heures voire quelques jours mon soft plante : un des thread que je ne métrise pas (un des thread du moteur JET je pense) reste en mode Wait paralysant toute mon application.
    J'ai pensé résoudre mon problème en abandonnant les driver ODBC et en utilisant la technologie DAO des MFC, mais il n'en est rien...
    Je viens également de me rendre compte que par moment mes enregistrements sont mélangés : j'utilise une clé primaire incrémentée automatiquement et pourtant quand je réouvre ma base avec Access on passe par exemple de l'enregistrement 120 à 128, les enregistrements 121 à 127 se trouvant entre les enregistrements 133 et 134 !!!

    est ce que ce mélange des enregistrements est normal sous Access ?
    est ce que certain ont déjà rencontré ce type de plantage du moteur JET ? (si c'est bien de lui dont il sagit...)
    est ce que les 2 problèmes sont liés ?

    Un grand merci à ceux qui vont me lire jusqu'au bout !!!

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 751
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 751
    Points : 2 368
    Points
    2 368
    Par défaut
    Bonjour,
    Citation Envoyé par SERTNM
    Ce soft enregistre ses résultats dans une base de donnée Access 97 ( 1 à 10 enregistrements par seconde ).
    La base de donnée était pilotée dans un premier temps par les drivers ODBC. Au bout de quelques heures voire quelques jours mon soft plante : un des thread que je ne métrise pas (un des thread du moteur JET je pense) reste en mode Wait paralysant toute mon application.
    J'ai pensé résoudre mon problème en abandonnant les driver ODBC et en utilisant la technologie DAO des MFC, mais il n'en est rien...
    STP, donne nous le code qui réalise l'insertion des enregistrements (tout le code, pas juste un extrait).

  3. #3
    Membre régulier
    Inscrit en
    Juillet 2006
    Messages
    75
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 75
    Points : 76
    Points
    76
    Par défaut
    Bon je vais essayé de faire simple parce que le code de mon app est assez volumineux :

    Il s'agit d'une application MFC Dialog based.
    La boite de dialogue me sert pour l'affichage des résultas et les interaction avec l'utilisateur,
    Un thread supplémentaire s'occupe de l'acquisition et du traitement des images ainsi que de l'enregistrement des donnés dans la base (1 à 10 résultats par seconde).
    Une relecture des résultats antérieurs ainsi qu'une gestion des utilisateurs dans la BDD font que plusieurs threads à travers différentes boites de dialogue sont succeptibles d'utiliser la BDD pour y lire ou écrire des choses de manière occasionnelle.

    Tout ceci m'a conduit à construire mon code de la manière suivante :
    - la base de donnée est une CDaoDatabase et est déclaré en global. Elle est ouverte une fois pour toute au début du programme.
    - le recordset est un CDaoRecorset lui aussi déclaré en global et initialisé une fois pour toute au début.
    - tous les accès à la base et/ou au recordset sont protègés par un mutex.

    Le thread qui s'occupe du traitement et de l'enregistrement reçoit comme paramètre un objet PROCESS (déclaré comme membre de la boite de dialogue de base) qui lui même contient un objet RESULTAS : à la fin de chaque traitement on appelle la fonction membre de RESULTAS permettant l'enregistrement :

    (les résultats sont stockés dans la table 'Images')

    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    int NResultats::WriteToDB()
    {
    	int result=0;
     
    	COleVariant varInt, varDbl, varL, varC;
    	varInt.vt = VT_I2;
    	varDbl.vt = VT_R8;
    	varL.vt = VT_I4;
    	varC.vt = VT_I1;
     
    	g_mutex.Lock();
    	g_pRecordset->Open(AFX_DAO_USE_DEFAULT_TYPE,
    					   "SELECT * FROM Images", dbAppendOnly);
     
    	if(g_pRecordset->CanAppend())
    	{
    		g_pRecordset->AddNew();
     
    		g_pRecordset->SetFieldValue("Chemin",LPCTSTR(cheminIm));
     
    		g_pRecordset->SetFieldValue( "Date",LPCTSTR(date.Format("%d/%m/%Y %H:%M:%S")) );
     
    		varL.lVal=SettingID;
    		g_pRecordset->SetFieldValue("ReglageID",varL);
     
    		varL.lVal=nMoule;
    		g_pRecordset->SetFieldValue("Moule",varL);
     
    		varInt.iVal=bIron;
    		g_pRecordset->SetFieldValue("bIron",varInt);
     
    		// Etc...... yen a une dizaine
     
    		g_pRecordset->Update();
    	}
    	else 
    	{
    		result=1;
    	}
     
    	g_pRecordset->Close();
    	g_mutex.Unlock();
    	Sleep(0);
     
    	return result;
    }
    Voilà j'espère que j'ai été assez précis.... PLEASE HELP !!!

    Encore merci à tous ceux qui ont le courage de jetter un oeil à ce topic

  4. #4
    Membre émérite

    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 751
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 751
    Points : 2 368
    Points
    2 368
    Par défaut
    Grâce à tes explications détaillées et vraiment très claires , je pense avoir compris le problème.

    Pour commencer, juste pour être rigoureux et mieux profiter des informations qui suivent, ce serait bien de mettre au clair les différentes versions des applications/composants impliqués:
    * Access 97, DAO 3.5, JET 3.5.
    * Access 2000 (et suivants ?), DAO 3.6, JET 4.0.

    Tu as développé une application multi-threads qui utilise des objets de base de données DAO; ces objets DAO sont partagés par les différents threads.
    Tu utilises un mutex, pensant que celà suffirait à éviter des conflits... et bien apparemment ça ne suffit pas, et c'est là que ça se passe.

    D'après ce que j'ai lu (et pas forcément vérifié par moi même):
    * Le moteur de bases de données JET ne serait pas thread-safe, quelle que soit sa version... (en particulier parce qu'il utilise une DLL VBA pour l'évaluation des expressions et que cette DLL n'est pas thread-safe).
    * Aucun driver ODBC pour JET ne serait thread-safe... (cf. le point précédent).
    * Quelle que soit la version de la DLL DAO, elle implémente un serveur OLE Automation in-process.
    * La bibliothèque DAO MFC VC++ n'est pas thread-safe (dommage car c'est celle que tu utilises ).
    * La bibliothèque DAO SDK VC++ pour Jet 3.5 (et suivant) est thread-safe dans sa seule version UNICODE (et pas la version ANSI).
    * En revanche, le fournisseur OLE DB (utilisé par ADO avec Jet 4.0) est nativement thread-safe (parce qu'il apporte sa propre DLL VBA thread-safe).

    Pour le thread-safe avec DAO SDK 3.5 UNICODE, il y a quand même une condition importante:
    Even with the DAO SDK and a UNICODE build, you need to create and terminate your DAO objects within the same thread.
    Un lien chez Microsoft, pour en savoir plus:
    Thread safety with Jet has been achieved with the release of DAO 3.5/3.6 in Visual C++
    http://support.microsoft.com/kb/169395/en-us

    Conclusion: tu dois absolument éviter de partager les objets DAO entre tes threads
    Citation Envoyé par SERTNM
    - la base de donnée est une CDaoDatabase et est déclaré en global. Elle est ouverte une fois pour toute au début du programme.
    - le recordset est un CDaoRecorset lui aussi déclaré en global et initialisé une fois pour toute au début.
    Chaque thread qui veut accéder au fichier MDB doit créer et gérer ses propres objets DAO (DBEngine, database, recordset...).
    Ca ne nécessite pas beaucoup de modifications.
    Ton application consommera plus de ressources mais sera stable.

    Sinon, tu peux migrer vers la bibliothèque DAO SDK (ça ne semble pas trop compliqué), voire même basculer vers ADO/OLE DB... si tu as le temps .

    Là encore, une alternative possible... (mais probablement pas thread-safe).
    How to use DAO in Visual C++ without MFC
    http://www.codeproject.com/useritems/cppdao.asp

    =-=-=-=-=-=

    En ce qui concerne ton code DAO, j'aurais bien quelques commentaires... mais ce post est suffisamment long comme ça .

  5. #5
    Expert éminent

    Avatar de Maxence HUBICHE
    Homme Profil pro
    Développeur SQLServer/Access
    Inscrit en
    Juin 2002
    Messages
    3 842
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Val d'Oise (Île de France)

    Informations professionnelles :
    Activité : Développeur SQLServer/Access

    Informations forums :
    Inscription : Juin 2002
    Messages : 3 842
    Points : 9 197
    Points
    9 197
    Par défaut
    Citation Envoyé par SERTNM
    est ce que ce mélange des enregistrements est normal sous Access ?
    principe de base dans les bases de données :

    Dans les tables, les enregistrements ne sont pas ordonnés.
    C'est la clause ORDER BY d'une requête qui va les ordonner.
    Par conséquent, l'ordre de ta clé primaire importe peu.

    Par contre, un timestamp aurait été amusant, pour savoir si oui ou non les enregistrements sont générés n'importe comment.

  6. #6
    Membre régulier
    Inscrit en
    Juillet 2006
    Messages
    75
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 75
    Points : 76
    Points
    76
    Par défaut
    Merci pour vos réponses je pense qu'elles vont faire avancer mon problème, je met la solution 'à chaque thread ses objet' en oeuvre dès ce matin.

    Il reste pour moi un seul point d'interrogation : pour tester mon soft dans le durée je le laisse tourner plusieurs jours sans y toucher, donc sans relecture de résultats dans la base et sans gestion d'utilisateurs : le seul thread 'actif' est celui qui traite les images et enregistre les résultats... Il n'y a donc pas d'accès par plusieurs thread dans la durée.
    Ceci dit au lancement du soft plusieurs thread se font concurence (log de l'utilisateur et lectures d'anciens réglages...) : est ce que cette phase de démarrage peut suffire à déboussoler JET pour la suite ?

    En ce qui concerne ton code DAO, j'aurais bien quelques commentaires...
    je suis preneur !!!

  7. #7
    Membre régulier
    Inscrit en
    Juillet 2006
    Messages
    75
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 75
    Points : 76
    Points
    76
    Par défaut
    Suite à ces précieux conseil j'ai supprimé mes variables globales : la base de données et le recordset ne sont plus que des variables locales au fonctions qui veulent accèder à la base

    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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    int NResultats::WriteToDB()
    {
    	int result=0;
    
    	COleVariant varInt, varDbl, varL, varC;
    	varInt.vt = VT_I2;
    	varDbl.vt = VT_R8;
    	varL.vt = VT_I4;
    	varC.vt = VT_I1;
    
    	g_mutex.Lock();
            CDaoDatabase Database;
            Database.Open("c:\\MyDB.mdb);
            CRecordset Recordset(&Database);
    	Recordset.Open(AFX_DAO_USE_DEFAULT_TYPE,
    					   "SELECT * FROM Images", dbAppendOnly);
    
    	if(Recordset.CanAppend())
    	{
    		Recordset.AddNew();
    		Recordset.SetFieldValue("Chemin",LPCTSTR(cheminIm));
    		
    		// Etc...... yen a une dizaine
    
    		Recordset.Update();
    	}
    	else 
    	{
    		result=1;
    	}
    
    	Recordset.Close();
            Database.Close();
    	g_mutex.Unlock();
    	Sleep(0);
    
    	return result;
    }
    ET bien çà marche pas !!! Si toute les fonctions utilisant ce genre de code sont appelées par le même thread c'est bon, mais si un autre thread vient s'en meler je me retrouve avec un crash au niveau de dao350.dll....

    Tout ceci parait être en accord avec les précédents posts. Mon soucis viend du fait que ne suffit pas pour arreter JET, d'où ma question : comment arreter le DBEngine ?

  8. #8
    Membre émérite

    Profil pro
    Inscrit en
    Février 2005
    Messages
    1 751
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 1 751
    Points : 2 368
    Points
    2 368
    Par défaut
    Citation Envoyé par SERTNM
    Si toute les fonctions utilisant ce genre de code sont appelées par le même thread c'est bon, mais si un autre thread vient s'en meler je me retrouve avec un crash au niveau de dao350.dll....

    Tout ceci parait être en accord avec les précédents posts. Mon soucis viend du fait que ne suffit pas pour arreter JET, d'où ma question : comment arreter le DBEngine ?
    La réponse est au bout du lien.

    DAO Database Engine Initialization and Termination
    http://msdn2.microsoft.com/en-us/library/5827d149.aspx

    Pour ton code DAO, tu peux déjà supprimer le bloc if(Recordset.CanAppend()), qui sert à tester si le type du Recordset permet les insertions.
    Par construction, ton Recordset est correct.

    Sinon, je te conseille plutôt d'utiliser une requête action "INSERT INTO" que tu éxécutes directement via la fonction membre Execute de l'objet Database.

    Syntaxe de la requête action d'insertion:
    INSERT INTO Images (Chemin, [Date], ReglageId)
    VALUES ("texte du chemin", #1/30/2007#, 15)
    Quel est le type du champ [Date]: CHAR(n) ou DATE ?
    Si c'est un type date, le format est #m/d/y H:M:S#.
    Date est un mot réservé du SQL Jet. Pour qu'il soit interprété comme un nom de champ, on l'entoure de crochets.

  9. #9
    Membre régulier
    Inscrit en
    Juillet 2006
    Messages
    75
    Détails du profil
    Informations forums :
    Inscription : Juillet 2006
    Messages : 75
    Points : 76
    Points
    76
    Par défaut
    Merci !
    j'ai ajouter la commande
    et tout semble bien se passer.... à voire dans la durée.

    Etant donnée mon application, Access est surdimensionné je trouve. Mais étant donné qu'au début de ce projet je suis parti de 0 en base de donné il a été très facile pour moi de le prendre en main.... maintenant que j'ai un peu plus de recule je me demande si utiliser SQLite ne serait pas plus judicieux... vous en pensez quoi ?

Discussions similaires

  1. ODBC, lecture seule et moteur Jet
    Par zouhenlai dans le forum Requêtes et SQL.
    Réponses: 2
    Dernier message: 03/11/2008, 11h29
  2. Macro complémentaire : Extension au Moteur Jet
    Par Jack78960 dans le forum Contribuez
    Réponses: 9
    Dernier message: 04/10/2008, 13h45
  3. Réponses: 2
    Dernier message: 26/03/2007, 10h52
  4. explications adp mdb moteur jet
    Par ash_rmy dans le forum Access
    Réponses: 3
    Dernier message: 22/09/2006, 07h54
  5. Moteur Jet
    Par Grosmou dans le forum Access
    Réponses: 8
    Dernier message: 05/12/2005, 13h04

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