2010-07-13 3 views
11

J'utilise ce code:Comment gérer les résultats des threads python?

def startThreads(arrayofkeywords): 
    global i 
    i = 0 
    while len(arrayofkeywords): 
     try: 
      if i<maxThreads: 
       keyword = arrayofkeywords.pop(0) 
       i = i+1 
       thread = doStuffWith(keyword) 
       thread.start() 
     except KeyboardInterrupt: 
      sys.exit() 
    thread.join() 

pour enfiler en python, je l'ai presque tout fait, mais je ne sais pas comment gérer les résultats de chaque fil, sur chaque fil que j'ai un tableau de chaînes comme résultat , comment puis-je joindre tous ces tableaux en un en toute sécurité? Parce que, si j'essaie d'écrire dans un tableau global, deux threads pourraient écrire en même temps.

Répondre

13

Tout d'abord, vous avez réellement besoin pour sauver tous ces objets thread appeler join() sur eux. Comme écrit, vous enregistrez seulement le dernier d'entre eux, et seulement s'il n'y a pas d'exception.

Un moyen facile de faire de la programmation multithread est de donner à chaque thread toutes les données dont il a besoin pour s'exécuter, et ensuite de ne pas écrire sur quoi que ce soit en dehors de cet ensemble de travail. Si tous les threads suivent cette directive, leurs écritures ne seront pas interférer les uns avec les autres. Ensuite, une fois qu'un thread a terminé, faites thread principal seulement agréger les résultats dans un tableau global. C'est ce que l'on appelle le "parallélisme fork/join".

Si vous sous-classez l'objet Thread, vous pouvez lui donner de l'espace pour stocker cette valeur de retour sans interférer avec les autres threads. Ensuite, vous pouvez faire quelque chose comme ceci:

class MyThread(threading.Thread): 
    def __init__(self, ...): 
     self.result = [] 
     ... 

def main(): 
    # doStuffWith() returns a MyThread instance 
    threads = [ doStuffWith(k).start() for k in arrayofkeywords[:maxThreads] ] 
    for t in threads: 
     t.join() 
     ret = t.result 
     # process return value here 

Edit:

Après avoir regardé un peu, il semble que la méthode ci-dessus isn't the preferred way to do threads in Python. Ce qui précède est plus d'un modèle Java-esque pour les discussions. Au lieu de cela, vous pouvez faire quelque chose comme:

def handler(outList) 
    ... 
    # Modify existing object (important!) 
    outList.append(1) 
    ... 

def doStuffWith(keyword): 
    ... 
    result = [] 
    thread = Thread(target=handler, args=(result,)) 
    return (thread, result) 

def main(): 
    threads = [ doStuffWith(k) for k in arrayofkeywords[:maxThreads] ] 
    for t in threads: 
     t[0].start() 
    for t in threads: 
     t[0].join() 
     ret = t[1] 
     # process return value here 
+0

Merci, votre réponse est la plus facile pour moi de comprendre, va l'essayer dès maintenant. – jahmax

3

Vous devez conserver des pointeurs sur chaque thread que vous créez. En l'état, votre code assure uniquement la fin du dernier thread créé. Cela n'implique pas que tous ceux que vous avez commencés avant ont également fini. Cela résout également le problème de l'accès en écriture car chaque thread va stocker ses données localement. Ensuite, après que tout soit fait, vous pouvez faire le travail de combiner les données locales de chaque thread.

+0

Et comment accéder à chaque donnée locale de thread? parce que les threads sont créés/finissant tout le temps, n'est pas toujours les mêmes 10 threads. – jahmax

+0

Par des données locales, je faisais allusion à la solution de Karmastan. En fonction de ce que vous avez posté dans la question, il semble que vous fassiez N threads, démarrez ensuite, puis rejoignez-les. Étant donné que ce modèle accédant aux données locales après la fin du thread fonctionnera correctement. Si vous voulez que les choses soient beaucoup plus dynamiques, alors vous voudrez regarder les réponses qui discutent le rassemblement de fil et stockent les résultats dans la file de données. – unholysampler

0

L'écriture dans un tableau global est correcte si vous utilisez un sémaphore pour protéger la section critique. Vous 'acquérez' le verrou lorsque vous souhaitez ajouter au tableau global, puis 'relâchez' lorsque vous avez terminé. De cette façon, un seul thread est ajouté à chaque tableau.

Consultez http://docs.python.org/library/threading.html et recherchez sémaphore pour plus d'informations.

sem = threading.Semaphore() 
... 
sem.acquire() 
# do dangerous stuff 
sem.release() 
13

Utilisez une instance Queue.Queue, intrinsèquement thread-safe. Chaque thread peut .put ses résultats à cette instance globale quand il est fait, et le thread principal (quand il sait que tous les threads de travail sont faits, par .join par exemple comme dans la réponse de @ unholysampler) peut en boucle chaque résultat, et utiliser chaque résultat à .extend la liste "résultat global", jusqu'à ce que la file d'attente soit vidée.

Modifier: il y a d'autres gros problèmes avec votre code - si le nombre maximum de threads est inférieur au nombre de mots-clés, il ne s'arrêtera jamais (vous essayez de démarrer un thread par mot-clé - jamais moins - mais si vous avez déjà commencé les nombres max vous bouclez pour toujours à aucune autre fin).

Tenir compte au lieu d'utiliser une piscine filetage, un peu comme celui de this recipe, sauf que, au lieu de faire la queue appelables vous file d'attente les mots-clés - depuis le appelable que vous voulez exécuter dans le fil est le même dans chaque fil, juste en variant l'argument. Bien sûr, cet appel sera modifié pour retirer quelque chose de la file d'attente des tâches entrantes (avec .get) et .put la liste des résultats dans la file d'attente des résultats sortants une fois terminé.

Pour terminer les fils N, vous pouvez, après tous les mots clés, .put N « factionnaires » (par exemple None, en supposant que mot-clé peut être None): appelable sortie volonté d'un fil si le « mot-clé » juste tiré est None.

Plus souvent qu'autrement, Queue.Queue offre la meilleure façon d'organiser les architectures de threading (et multiprocessing!) En Python, qu'elles soient génériques comme dans la recette que je vous ai indiquée, ou plus spécialisées comme je le suggère pour votre cas d'utilisation dans les deux derniers paragraphes.

1

Je sais que cette question est un peu vieux, mais la meilleure façon de le faire est de ne pas vous faire du mal trop de la manière proposée par d'autres collègues :)

Veuillez lire la référence sur Pool. De cette façon, vous allez joindre votre travail:

def doStuffWith(keyword): 
    return keyword + ' processed in thread' 

def startThreads(arrayofkeywords): 
    pool = Pool(processes=maxThreads) 
    result = pool.map(doStuffWith, arrayofkeywords) 
    print result 
Questions connexes