Précédent   Forum du club des développeurs et IT Pro > Environnements de développement > WinDev
WinDev Forum d'entraide sur la programmation en WinDev
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 29/12/2012, 19h35   #1
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/12/2012, 08h08   #2
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
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.
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/12/2012, 17h27   #3
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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...
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/12/2012, 17h42   #4
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
(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
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/12/2012, 19h25   #5
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/12/2012, 23h47   #6
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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!
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/12/2012, 09h07   #7
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
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
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/12/2012, 11h28   #8
themayu
Membre confirmé
 
Homme
Développeur informatique
Inscription : mars 2009
Messages : 92
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : mars 2009
Messages : 92
Points : 222
Points : 222
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
themayu est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/01/2013, 15h47   #9
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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?
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/01/2013, 16h56   #10
themayu
Membre confirmé
 
Homme
Développeur informatique
Inscription : mars 2009
Messages : 92
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : mars 2009
Messages : 92
Points : 222
Points : 222
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.
themayu est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/01/2013, 17h50   #11
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/01/2013, 18h53   #12
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
Bonsoir,
Est-ce que le problème se pose aussi si les mises à jours sont faites avec des ordres SqlExec ?

Tatayo.
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 01/01/2013, 21h58   #13
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/01/2013, 08h46   #14
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
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.
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/01/2013, 13h48   #15
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/01/2013, 14h42   #16
tatayo
Expert Confirmé
 
Homme
Responsable de service informatique
Inscription : janvier 2009
Messages : 1 545
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 39
Localisation : France

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Boutique - Magasin

Informations forums :
Inscription : janvier 2009
Messages : 1 545
Points : 3 089
Points : 3 089
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
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.
tatayo est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/01/2013, 18h02   #17
themayu
Membre confirmé
 
Homme
Développeur informatique
Inscription : mars 2009
Messages : 92
Détails du profil
Informations personnelles :
Sexe : Homme
Localisation : France

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : mars 2009
Messages : 92
Points : 222
Points : 222
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.
themayu est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 02/01/2013, 18h34   #18
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 05/01/2013, 14h43   #19
zouzoukha
Membre chevronné
 
Inscription : avril 2008
Messages : 723
Détails du profil
Informations forums :
Inscription : avril 2008
Messages : 723
Points : 623
Points : 623
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
zouzoukha est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 22h05.


 
 
 
 
Partenaires

Hébergement Web