2010-04-04 5 views
0

Les étiquettes peuvent ne pas être exactes puisque je ne suis pas sûr où le problème est.comportement étrange en python

J'ai un module où je suis en train de lire des données d'une socket, et écrire les résultats dans un fichier (ajouter) Il ressemble à quelque chose comme ça, (seulement les parties pertinentes inclus)

if __name__ == "__main__": 
    <some init code> 
    for line in file: 
     t = Thread(target=foo, args=(line,)) 
     t.start() 
    while nThreads > 0: 
     time.sleep(1) 

ici sont les autres modules,

def foo(text): 
    global countLock, nThreads 
    countLock.acquire() 
    nThreads += 1 
    countLock.release() 

    """connect to socket, send data, read response""" 
    writeResults(text, result) 

    countLock.acquire() 
    nThreads -= 1 
    countLock.release() 

def writeResults(text, result): 
    """acquire file lock""" 
    """append to file""" 
    """release file lock""" 

Maintenant, voici le problème. Au départ, j'ai eu une faute de frappe dans la fonction 'foo', où je passais la variable 'line' à writeResults au lieu de 'text'. 'line' n'est pas défini dans la fonction foo, il est défini dans le bloc principal, donc j'aurais dû voir une erreur, mais à la place ça fonctionnait bien, sauf que les données étaient ajoutées plusieurs fois au fichier, au lieu d'être écrites juste une fois, ce qui est le comportement requis, ce que j'ai eu quand j'ai corrigé la faute de frappe.

Ma question est,

1) Pourquoi ai-je pas une erreur?

2) Pourquoi la fonction writeResults a-t-elle été appelée plusieurs fois?

Répondre

1

Lorsque vous aviez (simplifiez la partie importante)

def foo(text): 
    writeResults(line, result) 

foo, ne pas avoir une variable locale line, utilisait la variable globale par ce nom ... qui se trouve être l'un ensemble (dans le fil principal) par for line in file:. Plus précisément, je m'attendrais à ce que le nombre total de lignes écrites soit OK: il y a un thread par ligne (une architecture bizarre, BTW) et chaque thread écrit une ligne ... le seul problème est, ligne chaque le fil écrit.

Dans votre intention, le premier thread écrit la première ligne, le deuxième thread écrit la deuxième ligne, etc; mais en réalité chaque thread écrira la ligne qui se trouve être liée au nom global line au moment crucial où le fil appelle writeResults. Ainsi, certaines lignes peuvent très bien être écrites plusieurs fois, d'autres non écrites. Par exemple, supposons que le thread principal s'exécute suffisamment vite pour démarrer tous les sous-threads avant que l'un d'entre eux n'écrive réellement. Dans ce cas, la valeur dernière prise par le nom global line (c'est-à-dire, la dernière ligne dans le fichier) serait celle écrite par tous les threads.Notez que même dans la version "corrigée" il n'y a aucune garantie sur l'ordre dans lequel les différentes lignes sont écrites, ce qui fait partie de ce qui rend cette architecture bizarre - normalement, puisque les lignes arrivent dans un certain ordre , vous voudriez conserver cet ordre en sortie. Je suppose que votre cas d'application est assez particulier pour ne pas avoir besoin de cette contrainte, mais je suis toujours perplexe que vous ayez besoin de tant de threads lorsque vous lisez un fichier et écrivez dans un fichier! -)

1

Vous devriez vraiment utiliser Thread.join() pour votre boucle principale d'attendre un fil pour terminer:

if __name__ == "__main__": 
    <some init code> 
    threads = [] 
    for line in file: 
     t = Thread(target=foo, args=(line,)) 
     t.start() 
     threads.append(t) 
    for t in threads: 
     t.join() 

et se débarrasser de la nThreads mondiale et countLock. En ce qui concerne vos questions, je ne sais pas pourquoi vous n'avez pas eu d'erreur (peut-être que l'exception a été mangée par quelque chose?), Et je me demande si le nombre de répétitions de writeResults correspond au nombre de lignes dans le fichier. Si c'était le cas, je devrais me demander si line est un global et chaque thread l'a écrit une fois.

+0

Merci pour votre réponse. Non, le nombre de répétitions (et j'aurais dû le mentionner auparavant) était de 4 ou 5 la plupart du temps. – fsm

1

Pour éviter le global variable appelée line, vous devriez écrire une fonction appelée main qui fait le travail. La variable est alors locale à la fonction main.

Faites votre programme ressembler à ceci:

def main(args): 
    # ... init ... 
    for line in file: 
     # ... process the line 
     pass 

if __name__ == "__main__": 
    main(sys.argv[1:])