pb avec la gestion des threads et le resultat sur la sortie std

pb avec la gestion des threads et le resultat sur la sortie std - Python - Programmation

Marsh Posté le 31-07-2008 à 11:46:34    

Bonjour,
 
Je suis actuellement en autoformation sur Python.
J'essaye de m'attaquer à la gestion des threads dans le cadre de mon boulot
(récupération d'information sur des postes XP du reseau via WMI)
 
La gestion des threads semble fonctionner correctement, mon seul problème, c'est que lorsque je souhaite ecrire dans un fichier texte ou bien affcher à l'ecran le resultat de ma recherche via WMI,
j'ai parfois le resultat de 2 threads sur la même ligne.
 
D'une part j'affiche avec print le resultat sur ma sortie std, et d'autre part dans un fichier uptime.csv.
Seulement de temps à autre j'ai le résultat suivant:
 
 
Suppression du fichier de resultat...
Debut du traitement a : Thu Jul 31 11:26:32 2008
 
Computername LastBootuUpTime Uptime (in Hour)    
poste1 07/31/2008 03:12:06 8    
poste17 07/30/2008 22:01:37 13    
poste25 07/30/2008 22:01:36 13    
poste33 07/30/2008 22:01:10 13    
poste45 07/28/2008 21:33:38 61    
poste47 07/30/2008 22:00:26 13      
poste16 07/31/2008 03:11:57 poste34 07/30/2008 21:59:42 WXP0064 07/30/2008 22:02:08 13
 
J'ai essayé de regarder du coté de Lock mais j'avoue avoir un peu de mal :-)
 
 
exemple de code:

Code :
  1. # -*- coding:utf-8 -*-
  2. from threading import Thread, Lock
  3. from time import sleep, ctime
  4. import Queue, time, random, pythoncom, wmi, os, re, win32com.client
  5. maxthreads =  20 # maximum number of concurrent threads
  6. loops = []
  7. class Worker(Thread):
  8.     def __init__(self, q):
  9.         Thread.__init__(self)
  10.         self.q  = q
  11.     def run(self):
  12.         all_done = 0
  13.         log = open('C:\\uptime.csv', 'a')
  14.         def WMIDateStringToDate(dtmDate):
  15.             strDateTime = ""
  16.             if (dtmDate[4] == 0):
  17.                 strDateTime = dtmDate[5] + '/'
  18.             else:
  19.                 strDateTime = dtmDate[4] + dtmDate[5] + '/'
  20.             if (dtmDate[6] == 0):
  21.                 strDateTime = strDateTime + dtmDate[7] + '/'
  22.             else:
  23.                 strDateTime = strDateTime + dtmDate[6] + dtmDate[7] + '/'
  24.                 strDateTime = strDateTime + dtmDate[0] + dtmDate[1] + dtmDate[2] + dtmDate[3] + " " + dtmDate[8] + dtmDate[9] + ":" + \
  25.                 dtmDate[10] + dtmDate[11] +':' + dtmDate[12] + dtmDate[13]
  26.             return strDateTime
  27.         while not all_done: 
  28.             try:
  29.                 table = self.q.get(0)
  30.                 pythoncom.CoInitialize()
  31.                 host = loops[table]
  32.                 #stdout_lock = Lock()
  33.                 #stdout_lock.acquire()
  34.                 try:
  35.                     strComputer = host
  36.                     objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator" )
  37.                     objSWbemServices = objWMIService.ConnectServer(strComputer,"root\cimv2" )
  38.                     colItems = objSWbemServices.ExecQuery("SELECT * FROM Win32_OperatingSystem" )
  39.                     colItems2 = objSWbemServices.ExecQuery("SELECT * FROM Win32_PerfFormattedData_PerfOS_System" )
  40.                     for objItem in colItems:
  41.                         log.write(host + ";" + WMIDateStringToDate(objItem.LastBootUpTime) + ";" )
  42.                         print host, WMIDateStringToDate(objItem.LastBootUpTime),
  43.                     for objItem2 in colItems2:
  44.                         up = int(objItem2.SystemUpTime)/60/60
  45.                         log.write(str(up) + "\n" )
  46.                         print up
  47.                 except:
  48.                     lPing = os.popen('ping '+ host + ' -n 1','r')
  49.                     sLigne = lPing.read()
  50.                     lResult = re.search('(perdus = 0)',sLigne)
  51.                     try:
  52.                         if ( len(lResult.groups(0)) == 1 ):
  53.                             print "Probleme de connexion via WMI sur " + host
  54.                     except:
  55.                         print "Pas de réponse au ping du " + host
  56.                 finally:
  57.                     pythoncom.CoUninitialize()
  58.                     #stdout_lock.release()
  59.             except Queue.Empty:
  60.                 all_done = 1
  61. if __name__ == "__main__":
  62.     if os.path.exists('C:\\uptime.csv'):
  63.         print "Suppression du fichier de resultat..."
  64.         os.remove('C:\\uptime.csv')
  65.     else:
  66.         print "Le fichier de resultat n'existe pas il sera cree automatiquement..."
  67.     print "Debut du traitement a :", time.ctime()
  68.     start = "Debut du traitement a : " + time.ctime()
  69.     fname = open('C:\\postes.txt', 'r')
  70.     for line in fname:
  71.         loops.append(line.strip('\n'))
  72.     fname.close()
  73.    
  74.     q = Queue.Queue()
  75.    
  76.     for i in range(len(loops)):
  77.         q.put(i)
  78.        
  79.     threads = []
  80.     for i in range(maxthreads):
  81.         t = Worker(q)
  82.         threads.append(t)
  83.         t.start()
  84.        
  85.     # wait for all threads to complete
  86.     for t in threads:
  87.         t.join();
  88.     print "Fin du traitement a :", time.ctime()


 
Mon problème se situe ici:

Code :
  1. strComputer = host
  2.                     objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator" )
  3.                     objSWbemServices = objWMIService.ConnectServer(strComputer,"root\cimv2" )
  4.                     colItems = objSWbemServices.ExecQuery("SELECT * FROM Win32_OperatingSystem" )
  5.                     colItems2 = objSWbemServices.ExecQuery("SELECT * FROM Win32_PerfFormattedData_PerfOS_System" )
  6.                     for objItem in colItems:
  7.                         log.write(host + ";" + WMIDateStringToDate(objItem.LastBootUpTime) + ";" )
  8.                         print host, WMIDateStringToDate(objItem.LastBootUpTime),
  9.                     for objItem2 in colItems2:
  10.                         up = int(objItem2.SystemUpTime)/60/60
  11.                         log.write(str(up) + "\n" )
  12.                         print up
  13.                 except:
  14.                     lPing = os.popen('ping '+ host + ' -n 1','r')
  15.                     sLigne = lPing.read()
  16.                     lResult = re.search('(perdus = 0)',sLigne)
  17.                     try:
  18.                         if ( len(lResult.groups(0)) == 1 ):
  19.                             print "Probleme de connexion via WMI sur " + host
  20.                     except:
  21.                         print "Pas de réponse au ping du " + host
  22.                 finally:
  23.                     pythoncom.CoUninitialize()
  24.                     #stdout_lock.release()
  25.             except Queue.Empty:
  26.                 all_done = 1


 
D'une part j'affiche avec print le resultat sur ma sortie std, et d'autre part dans un fichier uptime.csv.
Seulement de temps à autre j'ai le résultat suivant:
 
 
Suppression du fichier de resultat...
Debut du traitement a : Thu Jul 31 11:26:32 2008
 
Computername LastBootuUpTime Uptime (in Hour)    
poste1 07/31/2008 03:12:06 8    
poste17 07/30/2008 22:01:37 13    
poste25 07/30/2008 22:01:36 13    
poste33 07/30/2008 22:01:10 13    
poste45 07/28/2008 21:33:38 61    
poste47 07/30/2008 22:00:26 13      
poste16 07/31/2008 03:11:57 poste34 07/30/2008 21:59:42 WXP0064 07/30/2008 22:02:08 13
 
J'ai essayé de regardé du coté de Lock mais j'avoue avoir un peu de mal :-)


Message édité par Portanoo92 le 31-07-2008 à 12:42:52
Reply

Marsh Posté le 31-07-2008 à 11:46:34   

Reply

Marsh Posté le 01-08-2008 à 11:04:52    

Bon, c'est un peu le bordel :
 * t'as des noms de variables qui aident pas du tout (colItems, colItems2 ?)
 * la notation hongroise, quelle horreur [:pingouino]
 * et t'as le droit de rajouter des espaces et des retours à la ligne pour que ça soit lisible.
 
En plus, le résultat de ton fichier correspond pas à ton code (il manque les ";" entre les champs).
 
Bon, ceci dit, voici quelques pistes quand même.
 
Je pense pas que ta variable loops serve à grand chose (en plus d'avoir un nom super explicit [:pingouino]), pourquoi tu enregistres pas les noms d'hôtes que tu veux scanner directement dans l'objet Queue ?
 
Ensuite, Python dispose d'un type booléen, donc les trucs genre :

Code :
  1. all_done = 0
  2. while not all_done:
  3.    [...]
  4.    all_done = 1


ça marche, mais ça mérite la pelle à clou.
 
Surtout que tu pourrais faire plus simplement (et plus proprement aussi) :

Code :
  1. while True:
  2.    try:
  3.        host = self.q.get(0)
  4.    except Queue.Empty:
  5.        break


Ton catch de l'exception Queue.Empty concerne juste ce bout de code, pas besoin de tout englober, ça aide pas vraiment à la relecture.
 
 
Sinon, ton problème se situe là dedans :

Code :
  1. for objItem in colItems:
  2.    log.write(host + ";" + WMIDateStringToDate(objItem.LastBootUpTime) + ";" )
  3.  
  4. for objItem2 in colItems2:
  5.    up = int(objItem2.SystemUpTime)/60/60
  6.    log.write(str(up) + "\n" )


Forcément, tu commences à écrire ton nom d'hôte et la date de boot, puis après, tu écrit l'uptime avec un retour à la ligne. Si t'as un thread qui débarque entre les deux, plaf.
D'ailleurs, on dirait que t'as qu'un seul objet dans colItems et colItems2 (super nom nan ? [:petrus75] ) ... J'espère pour toi, tu risques d'avoir des suprises pour relire ton CSV après [:petrus75]

Reply

Marsh Posté le 01-08-2008 à 11:05:25    

Ah oui, et ya des fonctions dans la stdlib pour formatter les dates.

Reply

Marsh Posté le 01-08-2008 à 11:38:06    

Faut jamais écrire dans un fichier donné (ou une sortie donnée) depuis deux threads en même temps.
 
Crées plutôt un 3e thread qui va gérer le fichier de logging, et récupèrera ses résultats d'une Queue.Queue (via Queue.get() utilisé en blocker).
 
Accessoirement, Queue.get c'est un booléen qu'il prend, donc prière d'utiliser "False" (ou encore mieux "block=False" ) et non juste "0".
 
D'ailleurs, il serait encore plus intelligent d'utiliser Queue.get_nowait que Queue.get(False), c'est équivalent et infiniment plus clair.


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 01-08-2008 à 11:43:42    

Accessoirement, histoire d'éliminer ton flag de terminaison de threads, tu devrais regarder du côté de Queue.task_done et Queue.join


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 01-08-2008 à 11:44:29    

Ah ouais, j'ai confondu block et timeout dans Queue.get() :/
 
Sinon, c'est vrai que ça serait mieux de passer par un 3ème thread :jap:

Reply

Marsh Posté le 01-08-2008 à 11:47:33    

multani a écrit :

Ah ouais, j'ai confondu block et timeout dans Queue.get() :/
 
Sinon, c'est vrai que ça serait mieux de passer par un 3ème thread :jap:


D'ailleurs en passant ses Queue.get_nowait en Queue.get il pourrait commencer par lancer tous ses workers et ensuite seulement remplir la queue, histoire de perdre moins de temps :o


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 01-08-2008 à 11:56:03    

Certes :o
 
J'ai regardé Queue.task_done() et Queue.join(), chuis pas sûr de comprendre : si la queue est vide, le join() débloque le thread principal, mais du coup, les workers restent en attente sur le get() non ?
 
Sinon, en re-regardant la doc de get(), c'est moi ou c'est pas très malin d'avoir block et timeout ? [:pingouino]

Reply

Marsh Posté le 01-08-2008 à 12:21:59    

multani a écrit :

Certes :o
 
J'ai regardé Queue.task_done() et Queue.join(), chuis pas sûr de comprendre : si la queue est vide, le join() débloque le thread principal, mais du coup, les workers restent en attente sur le get() non ?


Oui, la pièce du puzzle qui te manque c'est Thread.setDaemon: quand thread principal se termine, le processus processus python meurt s'il ne reste que des threads daemonifiés.
 
Donc tu daemonifies le logger et tous les workers, quand les queues (work et log) sont terminées leurs join() libèrent le thread principal, qui se termine, ne restent que les loggers et workers qui sont daemonifiés donc le process python se termine et tue les threads [:dawa]

multani a écrit :

Sinon, en re-regardant la doc de get(), c'est moi ou c'est pas très malin d'avoir block et timeout ? [:pingouino]


timeout=0 pourrait avantageusement remplacer block=False (si ça a le bon comportement, mais je ne suis pas sûr de ce que fasse timeout=0, si ça se trouve il fait comme timeout=None: pas de timeout), mais timeout est arrivé bien après block :)
 
Par contre, je viens de me souvenir (et de voir dans la doc) que Queue.task_done et Queue.join sont arrivés dans Python 2.5, donc si portanoo92 est encore en 2.4 ou inférieur il ne les a pas et n'a d'autre choix que de join() sur les threads :jap:


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 01-08-2008 à 12:29:25    

masklinn a écrit :


Oui, la pièce du puzzle qui te manque c'est Thread.setDaemon: quand thread principal se termine, le processus processus python meurt s'il ne reste que des threads daemonifiés.
 
Donc tu daemonifies le logger et tous les workers, quand les queues (work et log) sont terminées leurs join() libèrent le thread principal, qui se termine, ne restent que les loggers et workers qui sont daemonifiés donc le process python se termine et tue les threads [:dawa]


[:romf]
 

masklinn a écrit :


timeout=0 pourrait avantageusement remplacer block=False (si ça a le bon comportement, mais je ne suis pas sûr de ce que fasse timeout=0, si ça se trouve il fait comme timeout=None: pas de timeout), mais timeout est arrivé bien après block :)


Ouais, j'imagine que c'est des histoires de compatibilité ... Mais du coup, ça fait réfléchir pour rien [:ocube]

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed