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

WinDev Discussion :

Problème contexte thread [WD17]


Sujet :

WinDev

  1. #1
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut Problème contexte thread
    Bonjour à tous!

    Après plusieurs semaines de recherche, je pense baisser les bras avec les threads

    Problème :

    Je vais essayer résumer l'essentiel:

    Avant, je tiens à souligner que je n'ai aucun problème sur l'utilisation des threads ni aucune erreur mais seulement des résultats incohérents dont je n'arrive pas à trouver la raison.
    J'ai un gros fichier Hyperfile d'articles de l'ordre de plusieurs millions d'enregistrements.
    Pour chacun des articles, je fais une requete HTTP afin de vérifier sur un site web l'existence de l'article.
    Si elle n'existe pas sur le site je l'ajoute à une liste.
    La rubrique Done de ARTICLE permet de savoir si la requete a été effectué pour un article donné!
    gBStop est une variable booléene pour stopper l'exécution des threads.

    Voici le code de ma procédure Check_New_Item()

    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
     
    PROCEDURE Check_New_Item(nBre,idPosition)
     
    HTTPCookieGère(Faux)		
     
    HLitRecherchePremier(ARTICLE,IDArticle,idPosition)
    TANTQUE PAS HEnDehors(ARTICLE)
     
    	SI ARTICLE.Done="" ALORS
    		ARTICLE.Done="ok"
    		HModifie(ARTICLE)
    		gnEnCours=ARTICLE.IDArticle
     
    		Message("Checking de "+ARTICLE.LIBELLE)
    		HTTPRequête(sUrl)
    		Multitâche(20)
    		sResult=HTTPDonneRésultat(httpRésultat)
     
    		SI ChaîneOccurrence(sResult,ARTICLE.LIBELLE,SansCasse)=0 ALORS
    			// Article existant - Ajout à ma liste
    			HRAZ(NOUVEL_ARTICLE)
    			NOUVEL_ARTICLE.IDArticle=ARTICLE.IDArticle
    			NOUVEL_ARTICLE.LIBELLE=ARTICLE.LIBELLE
    			HAjoute(NOUVEL_ARTICLE)
    			TableAffiche(TABLE_NOUVEL_ARTICLE)
    		FIN
    	FIN
    	SI gbStop=Vrai GOTO LBL_END
    	HLitSuivant(ARTICLE,IDArticle)
    FIN
     
    LBL_END :
    et voici le code de lancement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    POUR i=1 _A_ nbThreadMAX
    			ThreadExécute("Thread_Check_New_Item"+i,threadNormal,Check_New_Item,nTotal,gnEnCours)	
    			Multitâche(20)
    		FIN
     
    		POUR x=1 _A_ nbThreadMAX
    			TANTQUE ThreadEtat("Thread_Check_New_Item"+x)=threadEnCours
    				Multitâche(-500) // on rend la main à l'appli pendant 5 secondes
    			FIN
    		FIN
    Lorsque j'utilise nbThreadMAX=1 cela fonctionne à la perfection.
    La liste se remplit bien avec des articles n'existant pas sur le site.

    Problème :

    Lorsque j'augmente le nbre de threads à 5, j'ai plusieurs types d'erreurs non bloquantes:

    1°) De temps à autres une fenetre "mécanisme de sécurité" s'affiche disant que plusieurs utilisateurs tentent de modifier le même enregistrement ARTICLE ( je suppose lorsque je veux mettre Done à "ok")

    Comment éviter ce cas de figure?

    2°) il semblerait que lorsqu'il trouve pas un article sur le site en l'ajoutant, il ajoute également d'autres articles qui existent bien sur le site! (ce qui n'arrive jamais avec un seul thread)

    J'ai bien sûr essayer tous les modes d'exécution de threads (threadNormal,threadContexteGlobal,threadUtiliseHyperFile) sans succès!

    Merci d'avance à ceux qui pourrait m'aguiller sur ce coup

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    Salut Zouzoukha,
    Je vois déjà un problème dans ton code: les 5 threads traitent les mêmes articles, puisqu'ils partent avec 1 id de différence, et qu'ensuite ils balayent le fichier article "avec un pas de 1", et dans le même sens.
    Por ma part voilà ce que je ferai:
    1. je lancerai les threads avec l'option ThreadNormal, pour copier le context lors du lancement.
    2. dans chaque thread, je lirai les articles dans l'ordre, en tentant de les bloquer (option hBlocageLectureEcriture)
    Si la lecture réussi, l'enregistrement est bloqué et je peux le traiter. Sinon un autre thread l'a déjà bloqué, dans ce cas je passe au suivant.
    En quelque sorte je mets en place une "course" entre les threads, le premier à lire un enregistrement le traite.

    Il faut peut-être aussi mettre la partie du code qui crée NOUVEL_ARTICLE dans une section critique, à voir.

    Tatayo.

  3. #3
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut et merci Tatayo!

    Effectivement j'avais commencé par le blocage en écriture et cela fonctionnait apparemment...

    Le seul hic était que j'avais beaucoup de fenêtres intempestives avec compteur 10 secondes disant que l'enregistrement a été bloqué par un autre utilisateur!

    Y a t-il un moyen de désactiver ce message?

    Merci d'avance...

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    (réponse à prendre avec des pincettes galactiques, je n'utilise pas les ordres h*)
    Il me semble que ce message n'apparait que si tu ne testes pas le retour de hlit*.
    Ou alors il faut utiliser hSurErreur, je ne sais plus.

    Tatayo

  5. #5
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut Tatayo!

    +1 Pour HsurErreur

    Je n'ai plus le message de blocage, mais je remarque que lorsqu'il y a blocage j'ai toujours le souci (à savoir qu'il m'ajoute un article aléatoire )

    c'est terrible, je me demande s'il ne vaut pas mieux partager le fichier en autant de threads à lire?
    Ainsi chaque thread utilisera son propre fichier?
    Cela semble lourd, mais si je peux arriver à un résultat pourquoi pas?

    Merci encore

  6. #6
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut !!!

    Merci à Tatayo qui m'a permis d'avancer un petit peu

    Récapitulatif :

    Après avoir crée manuellement des threads "attaquant" chacun un fichier HF, je me rends compte que j'ai toujours le même problème cela m'a donc permis de voir que le problème se situait au moment ou j'enregistre dans un fichier hyperfile l'article non trouvé!

    En passant en mode debug, j'ai remarqué qu'à l'instant t lorsque le thread1 doit enregistrer un article A1, au même moment chacun des articles en cours sur les autres threads sont enregistrés!

    A croire que les threads s'executent en parralèle mais lorsqu'un des threads doit accéder à un fichier HF, tous les autres y accèdent immédiatement!

    Fameux Contexte ?

    Vais je être obligé de créer autant de fichiers d'enregistrement utilisé par les threads que le nombre de threads? (j'espère que non ...)

    Merci d'avance!

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    Et si tu crées un alias sur le fichier en question dans chaque thread, est-ce que tu as encore le problème ?

    Tatayo

  8. #8
    Membre averti
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2009
    Messages
    178
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2009
    Messages : 178
    Points : 416
    Points
    416
    Par défaut
    Bonjour,

    J'ai ne personnellement jamais réussi à faire fonctionner les contextes multiples avec les threads, ce en essayant toutes les options proposée par Windev.

    Le problème typique qui se présente est :
    1. Hraz pour initialiser
    2. écriture des valeures
    3. Lecture depuis un autre thread avec contexte
    4. Hajoute, le contenu correspond aux données lue par l'autre thread ...


    Pour palier ce problème, j'utilise systématiquement les requête SQL dans les threads, qui ne pose jamais de problème elles. (si on attribue bien l'identifiant !)

    Dans votre cas, je verrais bien quelque chose du genre :

    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
    PROCEDURE Check_New_Item(nBre est entier, Position est entier)
     
    SdArticle est source de donnée = DonneIdentifiant()
    HExécuteRequêteSQL(SdArticle , "SELECT * FROM ARTICLE LIMIT "+Position + ", "+nBre)
    POUR TOUT SdArticle
     
    	//... les traitements ...
    	// Pour mettre à jour l'article :
    	SdUpdate est source de donnée = DonneIdentifiant()
    	HExécuteRequêteSQL(SdUpdate ,"UPDATE ARTICLE SET Done="ok" WHERE ARTICLE.ID = " + ....)
     
    	//... les traitements ...
    	// Pour ajouter un article :
    	SdAjout est source de donnée = DonneIdentifiant()
    	HExécuteRequêteSQL(SdUpdate ,"INSERT INTO ARTICLE VALUES (......)")
     
    FIN

  9. #9
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut Themayu et merci pour l'intérêt!

    Cett instruction semble poser un problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SdArticle est source de donnée = DonneIdentifiant()
    J'obtiens l'erreur suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Un élément de type 'entier' ne peut pas être converti vers le type 'source de données'.
    Une idée?

  10. #10
    Membre averti
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2009
    Messages
    178
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2009
    Messages : 178
    Points : 416
    Points
    416
    Par défaut
    Bonjour

    L'identifiant doit donc attendre une chaîne (pas windev pour vérifier), vous pouvez essayer avec :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SdArticle est source de donnée = "SdArticle"+DonneIdentifiant()
    // Ou encore
    SdArticle est source de donnée = "SdArticle"+DonneGUID(guidBrut)

    Une discussion à ce sujet.

  11. #11
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut!
    Encore moi!

    Que je crée des alias ou des fichiers physiques différents pour chaque thread, au moment ou un thread enregistre dans son fichier un enregistrement tous les autres fichiers des autres threads se retrouvent également avec des enregistrements aléatoires!

    Je commence à saturer

    Edit : Désolé Themayu, je viens de voir ta réponse en retard!
    Je reprends donc de l'autre coté
    Merci

  12. #12
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    Bonsoir,
    Est-ce que le problème se pose aussi si les mises à jours sont faites avec des ordres SqlExec ?

    Tatayo.

  13. #13
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut Tatayo.

    J'ai essayé le code de Themayu en effectuant une requête sql et en parcourant les résultats de cette requête, j'ai utilisé des requête sql de modif et d'ajout (update et insert) :

    J'ai toujours le même souci dès que j'utilise plus d'un thread!

    Avec un seul thread ca fonctionne à la perfection!

    C'est maintenant que je commence à appréhender les difficultés ce fameux "contexte" hyperfile

  14. #14
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    Je pense que le problème vient du fait qu'aucun test n'est fait pour s'assurer qu'un article n'est traité qu'une seule fois, par un seul thread.
    Je vois peut-être une solution:
    1. Il te faut une procédure globale, avec une section critique. Le but de cette procédure est de renvoyer à chaque appel l'id suivant dans le fichier article. La section critique permet d'être sur de n'avoir qu'un seul appel à un instant donné.
    Comme les variables statiques n'existent pas, on peut imaginer que l'ID du dernier article traité est mémorisé dans une variable globale.
    2. Dans les threads, tu appelles cette procédure. Ensuite tu traites l'article en question avec le code de themayu (sans la boucle, évidemment). Si aucun id n'est renvoyé => fin du thread.

    Ainsi on est assuré qu'un article n'est traité que par un thread.

    Tatayo.

  15. #15
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut BISOUS TATYOOOOO !!!
    Salut!

    Tatayo, t'as vraiment de la chance de pas être une fille

    T'aurais eu un de ces smacks

    pour la section critique!

    Tout fonctionne à merveille!

    Je met le code de la procédure globale qui empêche un article d'être lu par plusieurs threads!

    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
    PROCEDURE Get_ID(nId est un entier)
     
    SectionCritiqueDébut("Lecture_ID")
    sTest est une chaîne
    SI nId=0 ALORS
    	nId++
    FIN
    HLitRecherchePremier(Article,IDArt,nId)
     
    TANTQUE PAS HEnDehors(Article)
    	SI Article.Done="" ALORS
    		Article.Done="ok"
    		HModifie(Article)
    		sTest="ok"
    		SORTIR		
    	FIN
    	HLitSuivant(Article,IDArt)
    FIN
    RENVOYER sTest
    SectionCritiqueFin("Lecture_ID")
    Le code du thread d'appel

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    TANTQUE gnIdEnCours<nTotal
    SI	Get_ID(gnIdEnCours) = "ok" ALORS
       // Traitement
        gnIdEnCours++
    FIN
    Note : Je trouve quand même l'opération avec 5 threads un peu plus lente! Serait-ce dû à l'accès limité à la lecture du fichier Article? ou une optimisation du code de Get_Id ?

    Merci en tout cas je n'ai plus de faux résultats

  16. #16
    Expert éminent sénior
    Homme Profil pro
    Responsable Données
    Inscrit en
    Janvier 2009
    Messages
    5 198
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable Données

    Informations forums :
    Inscription : Janvier 2009
    Messages : 5 198
    Points : 12 774
    Points
    12 774
    Par défaut
    En fait tel qu'est écrite ta procédure, tu ne peux avoir qu'un seul thread qui traite un article. Donc tu ne profites pas du multi-thread.
    Pour que ça marche, la procédure get_id doit se contenter de rechercher un id à traiter, et à le renvoyer. La variable globale n'est là que pour mémoriser l'Id en question entre chaque appel.
    On peut aussi imaginer un objet global, avec une méthode qui renvoe l'Id et un membre privé qui mémorise le dernier renvoyé.
    La recherche doit être dans une section critique.
    Par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    Procedure GetId:
    nId est un entier = -1
    sectioncritiquedebut("LectureID")
    sqlexec(chaineconstruit("select top 1 id from article where article.done <> 'OK' and id > %1 order by id desc",gnDernierId),"reqart")
    si sqlavance("reqart") = 0
    <div style="margin-left:40px">nId = sqllitcol("reqart",1)
    gnDernierId = nId</div>fin
    sqlferme("reqart")
    SectionCritiqueFin("LectureID")
    end
    Ensuite dans chaque thread tu récupères un Id en appelant la procédure, tu traites les articles et mets à jour les fichiers en utilisant SqlExec():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    nId est un entier
    nId = GetId()
    TantQue nId <> -1
    <div style="margin-left:40px">// Les traitements qui vont bien
    SqlExec("Update article set done = Ok where id = " + nId,"reqmaj")
    sqlferme("reqMaj")</div>Fin
    A tester, mais l'idée est là.

    Tatayo.

  17. #17
    Membre averti
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2009
    Messages
    178
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2009
    Messages : 178
    Points : 416
    Points
    416
    Par défaut
    Bonsoir,

    Pour votre problème de rapidité, c'est peut être due au placement du SectionCritiqueFin :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    RENVOYER sTest
    SectionCritiqueFin("Lecture_ID")
    L'instruction RENVOYER empêche son exécution, le programme doit donc attendre un autre SectionCritiqueFin("Lecture_ID") ailleurs dans le programme.

  18. #18
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut!

    @Tatayo,
    désolé, je n'y arrive pas avec les fonctions sqlexec...
    J'ai donc essayé avec les requêtes Hexecuterequetesql() mais toujours pareil au niveau des perfs (1 thread = 5 threads en terme de vitesse)

    @Themayu
    Effectivement j'avais testé en mettant sectioncritiquefin avant le renvoyer, mais j'obtenais des doublons comme auparavant (je suppose qu'il renvoyait la même valeur à tous les threads)
    C'est en placant renvoyer avant la fin section critique que cela fonctionnait!

    merci encore pour l'aide...

    J'avais été vite en besogne en mettant résolu

  19. #19
    Membre éprouvé
    Inscrit en
    Avril 2008
    Messages
    1 129
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 1 129
    Points : 1 283
    Points
    1 283
    Par défaut
    Salut et désolé de remonter le post

    Malgré mon multi-thread, je suis à la même vitesse qu'avec un thread!

    J'ai comme un goulot d'étranglement au niveau de GET_ID

    Une autre petite idée?

    merci d'avance

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

Discussions similaires

  1. Problème de thread : Plus de ressources système
    Par OliverSleep dans le forum C++Builder
    Réponses: 17
    Dernier message: 07/02/2006, 15h35
  2. [VB.NET] Problème de Thread
    Par Sadneth dans le forum ASP.NET
    Réponses: 26
    Dernier message: 31/01/2006, 10h12
  3. Problème synchronisation threads
    Par Linio dans le forum Concurrence et multi-thread
    Réponses: 19
    Dernier message: 11/01/2006, 16h57
  4. [MFC] Problème de Threads + Timers
    Par Invité dans le forum MFC
    Réponses: 8
    Dernier message: 30/11/2005, 10h51
  5. [VC++6][DX9] Problème de thread lors d'un blit ...
    Par grandjouff dans le forum DirectX
    Réponses: 2
    Dernier message: 12/06/2003, 22h22

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