Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 19 sur 19
  1. #1
    Membre chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 :
    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 :
    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mars 2009
    Messages
    133
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : mars 2009
    Messages : 133
    Points : 277
    Points
    277

    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 :
    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    Par défaut

    Salut Themayu et merci pour l'intérêt!

    Cett instruction semble poser un problème :

    Code :
    SdArticle est source de donnée = DonneIdentifiant()
    J'obtiens l'erreur suivante :

    Code :
    Un élément de type 'entier' ne peut pas être converti vers le type 'source de données'.
    Une idée?

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

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

    Informations forums :
    Inscription : mars 2009
    Messages : 133
    Points : 277
    Points
    277

    Par défaut

    Bonjour

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

    Code :
    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 :
    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 :
    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 Confirmé Sénior
    Homme Profil pro
    Développeur Freelance
    Inscrit en
    janvier 2009
    Messages
    2 234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Freelance

    Informations forums :
    Inscription : janvier 2009
    Messages : 2 234
    Points : 4 039
    Points
    4 039

    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 :
    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
    
    nId = sqllitcol("reqart",1) gnDernierId = nId
    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 :
    1
    2
    3
    4
    5
    6
    7
    nId est un entier
    nId = GetId()
    TantQue nId <> -1
    
    // Les traitements qui vont bien SqlExec("Update article set done = Ok where id = " + nId,"reqmaj") sqlferme("reqMaj")
    Fin
    A tester, mais l'idée est là.

    Tatayo.

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

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

    Informations forums :
    Inscription : mars 2009
    Messages : 133
    Points : 277
    Points
    277

    Par défaut

    Bonsoir,

    Pour votre problème de rapidité, c'est peut être due au placement du SectionCritiqueFin :
    Code :
    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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 chevronné
    Inscrit en
    avril 2008
    Messages
    793
    Détails du profil
    Informations forums :
    Inscription : avril 2008
    Messages : 793
    Points : 703
    Points
    703

    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.

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •