2010-05-21 12 views
17

Ho una serie di "attività" che vorrei eseguire in thread separati. Le attività devono essere eseguite da moduli separati. Ciascuno contiene la logica di business per l'elaborazione dei propri compiti.Creazione di una discussione in python

Data una tupla di attività, mi piacerebbe essere in grado di generare un nuovo thread per ciascun modulo come segue.

from foobar import alice, bob charles 
data = getWorkData() 
# these are enums (which I just found Python doesn't support natively) :(
tasks = (alice, bob, charles) 

for task in tasks 
    # Ok, just found out Python doesn't have a switch - @#$%! 
    # yet another thing I'll need help with then ... 
    switch 
    case alice: 
     #spawn thread here - how ? 
     alice.spawnWorker(data) 

Nessun premio per indovinare Sto ancora pensando in C++. Come posso scrivere questo in modo Python usando gli 'enum' e 'switch' di Pythonic ed essere in grado di eseguire un modulo in una nuova discussione.

Ovviamente, i moduli avranno tutti una classe derivata da un ABC (classe base astratta) chiamato Plugin. Il metodo spawnWorker() verrà dichiarato nell'interfaccia Plugin e definito nelle classi implementate nei vari moduli.

Forse, c'è un modo migliore (cioè Pythonic) per fare tutto questo ?. Sarei interessato a sapere

[Edit]

Ho appena letto un bot di più e sembra Python non implementa threading nel vero senso (almeno, non nel senso che un programmatore C++ avrebbe pensato). In ogni caso questo non è uno spettacolo per me. Ognuna delle attività richiede molto tempo e non desidero rallentare l'avvio di un'attività finché non ne è stata completata un'altra, ecco perché utilizzo la filettatura. L'affinamento del tempo non mi infastidisce molto - purché siano tutti avviati praticamente contemporaneamente (o poco dopo l'altro) Python può quindi sovrapporsi tra le pedate quanto vuole - per me va bene.

Ho visto una risposta a una domanda simile qui su SO.

Un utente fornisce una semplice classe per la filettatura come segue:

import threading 
class Foo (threading.Thread): 
    def __init__(self,x): 
     self.__x = x 
     threading.Thread.__init__(self) 
    def run (self): 
      print str(self.__x) 

for x in xrange(20): 
    Foo(x).start() 

Sto pensando di utilizzare questo per il mio ABC Plugin. La mia domanda allora è dove inserisco il codice in cui viene eseguito l'effettivo compito (cioè la logica aziendale). Presumo che questo vada nel metodo run() della classe Foo (ovvia domanda lo so, ma non voglio fare ipotesi).

È il mio pensiero sulla strada giusta o sbagliata (se difettoso -? Quello che ho perso)

+0

Invece di caso di commutazione, perché non utilizzare un polimorfismo appropriato (ereditarietà ABC o digitazione anatra)? – Santa

+0

@Santa: buon punto. Ecco come l'avrei fatto (polimorfismo) in C++. Ma non ero abbastanza sicuro se Python lo supportasse. – morpheous

+0

@morpheous Scoprirai che, oltre al tradizionale polimorfismo basato sull'ereditarietà, Python supporta anche approcci più dinamici al polimorfismo, il più importante dei quali è la digitazione anatra. – Santa

risposta

24

Invece di caso, perché non utilizzare un polimorfismo adeguato? Ad esempio, ecco cosa si può fare con la tipizzazione anatra in Python:

, per esempio, alice.py:

def do_stuff(data): 
    print 'alice does stuff with %s' % data 

, per esempio, bob.py:

def do_stuff(data): 
    print 'bob does stuff with %s' % data 

Poi nel codice del client, dicono, main.py:

import threading 
import alice, bob 

def get_work_data(): 
    return 'data' 

def main(): 
    tasks = [alice.do_stuff, bob.do_stuff] 
    data = get_work_data() 
    for task in tasks: 
     t = threading.Thread(target=task, args=(data,)) 
     t.start() 

fatemi sapere se ho bisogno di chiarire.

+0

+1 il tuo codice è bello e semplice - tuttavia, non stai passando i dati ai thread generati - potresti modificare il tuo codice per mostrare come i dati vengono passati al thread generati (come nel mio pseudocodice)?tx – morpheous

+6

Solo una nota che se 'data' sembra essere mutabile, ti consigliamo di passare una copia a ogni Thread, o anche passare un oggetto lock (http://docs.python.org/library/threading.html # lock-oggetti). – tgray

1

Python può contenere funzioni come oggetti. Per superare la limitazione della mancanza di un interruttore mi permetto di suggerire quanto segue:

case_alice = lambda data : alice.spawnWorker(data) 

my_dict[alice] = case_alice 

formando un dizionario di tenere le "caso" dichiarazioni.

Colgo ancora di più:

data = getWorkData() 
case_alice = lambda d : alice.spawnWorker(d) 
case_bob = lambda d : bob.spawnWorker(d) 
case_charles = lambda d : charles.spawnWorker(d) 

switch = { alice : case_alice, bob : case_bob, charles : case_charles } 
spawn = lambda person : switch[ person ](data) 
[ spawn(item) for item in (alice, bob, charles)] 
+0

@wheaties: +1 per il codice Pythonic (continuando a leggerlo per essere sicuro di capire tutto quello che sta succedendo lì). Potresti estendere un po 'il tuo sinppet con il modo di usare il modulo multiprocessing per generare in realtà i thread (o processi - non importa) – morpheous

+4

Il 'lambda' è del tutto inutile. Semplicemente 'my_dict [alice] = alice.spawnWorker', e' [cambia [elemento] (dati) per l'elemento in ...] ' –

+0

@morpheous Nessuna esperienza con la libreria di multiprocessing. @ Thomas Wouters Hai assolutamente ragione. Tuttavia, penso che lasciarli così come sono è più favorevole alla comprensione delle funzioni come oggetti di prima classe. Quando stavo imparando per la prima volta, vedere lambda mi ha ricordato questo. – wheaties

2

sequenziale esecuzione

from foobar import alice, bob charles 

for fct in (alice, bob charles): 
    fct() 

esecuzione parallela

from threading import Thread 
from foobar import alice, bob charles 

for fct in (alice, bob charles): 
    Thread(target=fct).start() 
+0

Thread ha il metodo 'run', non' start' – nkrkv

+1

@nailxx il metodo 'run' è dove si definisce il lavoro necessario nell'esecuzione di quel thread. Il metodo 'start' è ciò che è necessario inviare all'oggetto thread per eseguire il suo' run' in un thread di esecuzione separato. Altrimenti, lo stai solo eseguendo nel thread corrente, quindi sconfiggendo lo scopo di avere un 'Thread' definito per cominciare. – Santa

+0

@nailxx, ho inserito un collegamento alla documentazione che spiega che in un commento sul tuo post. – tgray

4
import threading 
from foobar import alice, bob, charles 

data = get_work_data() # names_in_pep8 are more Pythonic than camelCased 

for mod in [alice, bob, charles]: 
    # mod is an object that represent a module 
    worker = getattr(mod, 'do_work') 
    # worker now is a reference to the function like alice.do_work 
    t = threading.Thread(target=worker, args=[data]) 
    # uncomment following line if you don't want to block the program 
    # until thread finishes on termination 
    #t.daemon = True 
    t.start() 

Metti la tua logica in do_work funzioni corrispondenti mo Dules.

+0

Buona risposta, ma l'ultima riga dovrebbe essere' t.start() '. – tgray

+0

http://docs.python.org/library/threading.html#thread-objects – tgray

+0

+1 Mi piace molto questa risposta perché sembra che io possa scorrere direttamente sui moduli (puoi confermare che è il caso? essere così bello). Se la risposta è sì, significa che fintanto che ogni modulo ha una funzione chiamata 'do_work', allora il codice sopra genererà i thread ed eseguirà la funzione do_work() in ciascuno dei moduli in thread separati (la mia comprensione è corretta ?). Sembra che l'ultima chiamata al metodo debba essere start() anche se è corretta? – morpheous

Problemi correlati