2010-05-21 19 views
17

Tengo una serie de 'tareas' que me gustaría ejecutar en hilos separados. Las tareas deben ser realizadas por módulos separados. Cada uno contiene la lógica comercial para procesar sus tareas.Engendrando un hilo en python

Dando una tupla de tareas, me gustaría poder engendrar un nuevo hilo para cada módulo de la siguiente manera.

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) 

No hay premios para adivinar Todavía estoy pensando en C++. ¿Cómo puedo escribir esto de una manera Pythonic usando Pythonic 'enums' y 'switch'es, y ser capaz de ejecutar un módulo en un nuevo hilo.

Obviamente, los módulos tendrán una clase que se deriva de un ABC (clase base abstracta) llamado Plugin. El método spawnWorker() se declarará en la interfaz del complemento y se definirá en las clases implementadas en los diversos módulos.

Tal vez, hay una forma mejor (es decir, pitonica) de hacer todo esto ?. Yo estaría interesado en saber

[Editar]

He estado leyendo un bot más y parece que Python no implementa roscado en el verdadero sentido (al menos, no en el sentido de que un programador de C++ pensaría). En cualquier caso, eso no es un obstáculo para mí. Cada una de las tareas lleva bastante tiempo, y no quiero retrasar el inicio de una tarea hasta que otra se haya completado, por eso estoy usando el enhebrado. Cortar el tiempo no me molesta demasiado, siempre y cuando todos se inicien casi al mismo tiempo (o poco después) Python puede hacer un ciclo de tiempo entre las huellas tanto como quiera, está bien para mí.

He visto la respuesta a una pregunta similar aquí en SO.

un usuario proporciona una clase simple para el roscado de la siguiente manera:

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() 

estoy pensando en usar esto para mi ABC Plugin. Mi pregunta es, entonces, ¿dónde coloco el código donde se realiza la tarea real (es decir, la lógica de negocios). Supongo que esto va en el método run() de la clase Foo (pregunta obvia que sé, pero no quiero hacer ninguna suposición).

Es mi forma de pensar en el camino correcto o defectuoso (si es defectuoso - lo que me he perdido)

+0

En lugar de la caja del conmutador, ¿por qué no utilizar un polimorfismo apropiado (herencia ABC o tipado de pato)? – Santa

+0

@Santa: Buen punto. Así es como lo habría hecho (polimorfismo) en C++. Pero no estaba muy seguro de si Python lo soportaba. – morpheous

+0

@morpheous Encontrará que, además del polimorfismo tradicional basado en la herencia, Python también admite enfoques más dinámicos al polimorfismo, el más destacado de los cuales es el tipado de patos. – Santa

Respuesta

24

En lugar de la caja del interruptor, ¿por qué no utilizar un polimorfismo apropiado? Por ejemplo, aquí lo que se puede hacer con la tipificación de pato en Python:

en, digamos, alice.py:

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

en, digamos, bob.py:

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

Luego, en su código de cliente, decir, 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() 

Avísame si necesito aclarar.

+0

+1 su código es simple y simple; sin embargo, no está transfiriendo los datos a los hilos generados, ¿podría modificar su código para mostrar cómo se pasan los datos al hilos engendrados (como en mi pseudocódigo)?tx – morpheous

+6

Solo tenga en cuenta que si 'data' es mutable, querrá pasar una copia a cada Thread, o también pasar un objeto de bloqueo (http://docs.python.org/library/threading.html # lock-objects). – tgray

1

Python puede contener funciones como objetos. Para superar la limitación de que carecen de un interruptor puedo sugerir lo siguiente:

case_alice = lambda data : alice.spawnWorker(data) 

my_dict[alice] = case_alice 

formando un diccionario para mantener sus estados de "caso".

Quiero aprovechar aún más:

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 para el código Pythonic (todavía lo leo para asegurarme de que entiendo todo lo que sucede allí). ¿Podría ampliar un poco su sinpetta con cómo usar el módulo de multiprocesamiento para engendrar realmente los hilos (o procesos? No importa) – morpheous

+4

El 'lambda' es completamente innecesario. Simplemente haga 'my_dict [alice] = alice.spawnWorker', y' [switch [item] (data) para el elemento en ...] ' –

+0

@morpheous No hay experiencia con la biblioteca de multiprocesamiento. @Thomas Wouters Estás en lo cierto. Sin embargo, creo que dejarlos tal como están es más propicio para entender las funciones como objetos de primera clase. Cuando estaba aprendiendo por primera vez, ver a lambda me recordó eso. – wheaties

2

ejecución secuencial

from foobar import alice, bob charles 

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

ejecución paralela

from threading import Thread 
from foobar import alice, bob charles 

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

El subproceso tiene el método 'run', no' start' – nkrkv

+1

@nailxx El método 'run' es donde se define el trabajo necesario en la ejecución de ese subproceso. El método 'start' es lo que necesita enviar al objeto thread para hacer su' run' en un hilo de ejecución separado. De lo contrario, lo está ejecutando en el hilo actual, por lo tanto, para evitar el propósito de tener un 'Thread' definido para empezar. – Santa

+0

@nailxx, puse un enlace a la documentación que explica eso en un comentario sobre su publicación. – 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() 

Ponga su lógica en do_work funciones del correspondiente mes dules.

+0

Buena respuesta, pero su última línea debería ser' t.start() '. – tgray

+0

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

+0

+1 Me gusta mucho esta respuesta porque parece que puedo iterar directamente sobre los módulos (¿puede confirmar que es el caso? se tan genial). Si la respuesta es sí, significa que siempre que cada módulo tenga una función llamada 'do_work', el código anterior generará los hilos y ejecutará la función do_work() en cada uno de los módulos en hilos separados (¿es correcto mi entendimiento? ?). Parece que la última invocación al método debería ser start(), ¿verdad? – morpheous

Cuestiones relacionadas