OVH Cloud OVH Cloud

pb d'execution d'un thread

4 réponses
Avatar
Jack
Bonjour,



Je travaille actuellement sur le développement d'un script python permettant
d'utiliser le gant P5 glove sous blender.

Le système est divisé en deux partie une application Java récupère les
données du gant et reconnaît les geste effectué par l'utilisateur.

L'appli Java intègre un client qui envoie par mécanisme de socket les
résultat du traitement à l'appli Python.

Au sein de mon appli python j'ai crée un thread qui écoute et réceptionne en
permanence les paquets venant du client.

L'appli python est composée de d'une petit interface graphique spécifique à
Blender et d'une fonction qui déplace un objet en fonction des coordonnées
du gant. Au sein du thread "réception" il y une boucle infinie, mais ce que
je ne comprend pas c'est qu'il y a execution d'un tour de boucle uniquement
si l'interface capte un événement (mouvement de souris, ou pression d'une
touche). Ainsi quand je bouge la souris d'un pixel la boucle fait un tour
....

Je ne comprend pas trop, le thread n'est-il pas sensé être indépendant ?
Comment pourrait on forcer son exécution alors que je n'ai pas de boucle
dans mon code principal (il existe une boucle qui attend les événements mais
je n'ai pas d'accès a son code et donc ne peux pas mettre de pause).

quelqu'un a t'il déjà eu ce type de problème ?



Merci d'avance

le source de application python est disponible à l'adresse suivante:

http://daussy.dyndns.biz/~jacques/pt2/src/v2/AppliPython/serveurTh.py

4 réponses

Avatar
Jack
# Application de gestion du gant P5Glove sous blender
# Antoine ANDRE, Jacques DAUSSY
# Lib Python 2.3 minimun pour utiliser Socket et thread

# Importe socket..
import Blender
import time
import thread
from Blender import NMesh
from Blender import Object
from Blender.BGL import *
from Blender.Draw import *
from socket import *
import sys

#Events
EVENT_EXIT=3
EVENT_ACTIVER=1
EVENT_DESACTIVER=2


def gui(): # interface graphique pour blender

glClearColor(0.9,0.9,0.9,1)
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0,0,0) # defnie la couleur de la fenetre
glRasterPos2d(8, 320) # defnie une postion
Text("P5 Glove / Blender") # texte commentaire
glRasterPos2d(8, 300) # definie une position
Text("ESC pour quitter") # texte commentaire

Button("Activer",EVENT_ACTIVER,100,10,80,19) # Bouton pour
activer le pilotage
Button("Désactiver",EVENT_DESACTIVER,190,10,80,19) # Bouton pour
déactiver le pilotage
Button("Exit",EVENT_EXIT,280,10,80,19) # Bouton de sortie

def event(evt, val): # gestion des evenements
if (evt==ESCKEY): # Press la touche echap
Exit() # quitte l'execution du script
Redraw(1)
return

def BtnEvent (evt): # gestion des Evenements bouton
if(evt==EVENT_ACTIVER):
th=thread.start_new_thread(reception,()) # lancement du thread de
reception des paquets
if(evt==EVENT_DESACTIVER):
th.exit() #deactiver le thread
if (evt==EVENT_EXIT):
Exit()
Redraw(1)
return

def ClientStop(): # lorsque que le client s'arrete
print "Le client a quitte"
UDPSock.close()

def MoveCub(x,y,z): # fonction deplace un cube selon les coordonnées du gant
dans l'espace
print "nx:",x,"y:",y,"z:",z # affiche les coordonnées du gant
obj=Blender.Object.Get('Cube')
obj.setEuler(0,0,0)
obj.setLocation(x,y,z) #deplace le cube selon les coordonnées du gant
obj.select(1)
#print str(obj)
#sys.stdout.flush()



def reception(): # fonction thread qui reception les paquets du
client
host = "localhost" # Adresse du serveur
port = 12345 # Port du serveur
buf = 1024 # Taille du Buffer
addr = (host,port)
UDPSock = socket(AF_INET,SOCK_DGRAM) #creation du socket (type UDP)
UDPSock.bind(addr) # On bind l'adresse
print "in thread"
while 1:
x="" # coordonnée du gant dans l'espace
y=""
z=""
data,addr = UDPSock.recvfrom(buf) # recuperation des paquets
i=0 # ci dessous bout de code permettant de
decoupé les paquets pour recuperer les coordonnés et les stocker dans les
variables x,y,z
while(data[i+1]!="y"):
x=x+data[i+1]
i=i+1
i=i+1
while(data[i+1]!="z"):
y=y+data[i+1]
i=i+1
i=i+1
while(data[i+1]!="/"):
z=z+data[i+1]
i=i+1 # Fin du decoupage des données
MoveCub(float(x),float(y),float(z)) #appel de la fonction qui
deplace le cube



print "Interface execute, cliquer sur Activer pour lancer le thread Server"
Register(gui,event,BtnEvent) # lancement de l'interface graphique
Avatar
Amaury Forgeot d'Arc
Bonjour,
...

Au sein de mon appli python j'ai crée un thread qui écoute et réceptionne en
permanence les paquets venant du client.

L'appli python est composée de d'une petit interface graphique spécifique à
Blender et d'une fonction qui déplace un objet en fonction des coordonnées
du gant. Au sein du thread "réception" il y une boucle infinie, mais ce que
je ne comprend pas c'est qu'il y a execution d'un tour de boucle uniquement
si l'interface capte un événement (mouvement de souris, ou pression d'une
touche). Ainsi quand je bouge la souris d'un pixel la boucle fait un tour
....

Je ne comprend pas trop, le thread n'est-il pas sensé être indépendant ?
Comment pourrait on forcer son exécution alors que je n'ai pas de boucle
dans mon code principal (il existe une boucle qui attend les événements mais
je n'ai pas d'accès a son code et donc ne peux pas mettre de pause).

quelqu'un a t'il déjà eu ce type de problème ?

Merci d'avance



Je ne connais pas Blender, mais ce genre de comportement me rappelle des
soucis avec le GIL de python. Je m'explique :

L'exécution de Python n'est pas réellement multi-threads. Quand
plusieurs threads veulent exécuter du python, ils se partagent le GIL
(Global Interpreter Lock). Seul le thread qui possède le GIL peut
avancer dans l'exécution de son code python.

Le GIL peut passer d'un thread à l'autre par deux mécanismes : d'abord,
l'interpréteur force ce passage toutes les 100 instructions (je crois,
vérifie sys.getcheckinterval()) ; ensuite, quand une fonction le relâche
explicitement avant de faire une opération longue en C.
Par exemple, la fonction socket.receive() relâche le GIL pendant qu'elle
attend des données, puis le redemande avant de retourner son résultat au
code appelant.

Ton problème peut s'interpréter comme ceci : comme la fonction Register
ne relâche pas le GIL, le thread "réception" ne tourne pas.
Quand l'interface capte un événement, elle appelle les fonctions "event"
et "BntEvent" ce qui au bout de 100 instructions passe la main au thread
"réception". Et bien sûr, le thread "réception" relâche le GIL quand il
tente de lire un paquet.

Si mon interprétation est bonne, je ne vois malheureusement pas de
solution autre que de réécrire Blender pour le forcer à relâcher ce
fameux GIL...

Amaury.

Avatar
F. Petitjean
# Application de gestion du gant P5Glove sous blender
# Antoine ANDRE, Jacques DAUSSY
# Lib Python 2.3 minimun pour utiliser Socket et thread

# Importe socket..
import Blender
import time
import thread
from Blender import NMesh
from Blender import Object
from Blender.BGL import *
from Blender.Draw import *
from socket import *
S'il vous plaît n'utilisez pas from module import *

import sys

#Events
EVENT_EXIT=3
EVENT_ACTIVER=1
EVENT_DESACTIVER=2


def gui(): # interface graphique pour blender

glClearColor(0.9,0.9,0.9,1)
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0,0,0) # defnie la couleur de la fenetre
glRasterPos2d(8, 320) # defnie une postion
Text("P5 Glove / Blender") # texte commentaire
glRasterPos2d(8, 300) # definie une position
Text("ESC pour quitter") # texte commentaire

Button("Activer",EVENT_ACTIVER,100,10,80,19) # Bouton pour
activer le pilotage
Button("Désactiver",EVENT_DESACTIVER,190,10,80,19) # Bouton pour
déactiver le pilotage
Button("Exit",EVENT_EXIT,280,10,80,19) # Bouton de sortie

def event(evt, val): # gestion des evenements
if (evt==ESCKEY): # Press la touche echap
Exit() # quitte l'execution du script
Redraw(1)
return

def BtnEvent (evt): # gestion des Evenements bouton
if(evt==EVENT_ACTIVER):
th=thread.start_new_thread(reception,()) # lancement du thread de
reception des paquets
if(evt==EVENT_DESACTIVER):
th.exit() #deactiver le thread
if (evt==EVENT_EXIT):
Exit()
Redraw(1)
return
Il me semble que utiliser le module thread au lieu de threading

n'est pas une super idée pour un débutant : imaginer ce que devient la
variable essentielle th qui identifie le thread lorsque la fonction se
termine ? Comme toute variable locale elle est envoyée par gc (garbage
collector) dans le cyber-espace.
Je vous conseille de reprendre le script avec une classe dérivée de
threading.Thread et éventiellement une classe qui permette de lier le
tout (th sera alors un attrinut de cette classe Gant et donc ne sera
pas « garbage collected » tant qu'il y aura une référence gant.th sur
cet objet).
def ClientStop(): # lorsque que le client s'arrete
print "Le client a quitte"
UDPSock.close()
UDPSock vient d'où ?


def MoveCub(x,y,z): # fonction deplace un cube selon les coordonnées du gant
dans l'espace
print "nx:",x,"y:",y,"z:",z # affiche les coordonnées du gant
obj=Blender.Object.Get('Cube')
obj.setEuler(0,0,0)
obj.setLocation(x,y,z) #deplace le cube selon les coordonnées du gant
obj.select(1)
#print str(obj)
#sys.stdout.flush()



def reception(): # fonction thread qui reception les paquets du
client
host = "localhost" # Adresse du serveur
port = 12345 # Port du serveur
buf = 1024 # Taille du Buffer
addr = (host,port)
UDPSock = socket(AF_INET,SOCK_DGRAM) #creation du socket (type UDP)
UDPSock.bind(addr) # On bind l'adresse
print "in thread"
while 1:
x="" # coordonnée du gant dans l'espace
y=""
z=""
data,addr = UDPSock.recvfrom(buf) # recuperation des paquets
i=0 # ci dessous bout de code permettant de
decoupé les paquets pour recuperer les coordonnés et les stocker dans les
variables x,y,z
while(data[i+1]!="y"):
x=x+data[i+1]
i=i+1
i=i+1
while(data[i+1]!="z"):
y=y+data[i+1]
i=i+1
i=i+1
while(data[i+1]!="/"):
z=z+data[i+1]
i=i+1 # Fin du decoupage des données
MoveCub(float(x),float(y),float(z)) #appel de la fonction qui
deplace le cube



print "Interface execute, cliquer sur Activer pour lancer le thread Server"
Register(gui,event,BtnEvent) # lancement de l'interface graphique
Ce Register vient d'où ? (vous auriez pu ajouter une petite explication)






Avatar
Franck
Comment pourrait on forcer son exécution alors que je n'ai pas de boucle
dans mon code principal (il existe une boucle qui attend les événements mais
je n'ai pas d'accès a son code et donc ne peux pas mettre de pause).

quelqu'un a t'il déjà eu ce type de problème ?


J'ai eu ce problème avec PyGTK dont la boucle d'événement semble garder
la main (ou le GIL comme nous l'explique Amaury). Dans mon cas, j'ai
résolu le problème par des appels à time.sleep avec de petites valeurs
(0.1 par exemple).

Franck