J'ai récemment commencé avec le module de threading de python. Après quelques essais et erreurs, j'ai réussi à faire fonctionner le filetage de base en utilisant l'exemple de code suivant donné dans la plupart des didacticiels.
class SomeThread(threading.Thread):
def __init__(self, count):
threading.Thread.__init__(self)
def run(self):
print "Do something"
Mon problème est: j'ai une classe qui a des variables de classe et une fonction que je veux exécuter dans un thread séparé. Cependant, la fonction utilise des variables de classe et écrit également dans des variables de classe. Ainsi:
class MyClass:
somevar = 'someval'
def func_to_be_threaded(self):
# Uses other class functions
# Do something with class variables
Alors, comment pourrais-je essentiellement "mettre la classe de threads dans MyClass". Ainsi, si MyClass (). Func_to_threaded () est appelé, il s'exécutera dans un thread.
Si je comprends bien, vous souhaitez exécuter une fonction dans un thread séparé? Il y a plusieurs façons de procéder. Mais en gros, vous enveloppez votre fonction comme ceci:
class MyClass:
somevar = 'someval'
def _func_to_be_threaded(self):
# main body
def func_to_be_threaded(self):
threading.Thread(target=self._func_to_be_threaded).start()
Il peut être raccourci avec un décorateur:
def threaded(fn):
def wrapper(*args, **kwargs):
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
return wrapper
class MyClass:
somevar = 'someval'
@threaded
def func_to_be_threaded(self):
# main body
Modifier Version mise à jour avec un handle:
def threaded(fn):
def wrapper(*args, **kwargs):
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start()
return thread
return wrapper
class MyClass:
somevar = 'someval'
@threaded
def func_to_be_threaded(self):
print 'xyz'
Cela peut être utilisé comme suit:
>>> my_obj = MyClass()
>>> handle = my_obj.func_to_be_threaded()
>>> handle.join()
Il est maintenant possible de l'étendre encore plus si vous souhaitez renvoyer une valeur de la fonction. Considère ceci:
from threading import Thread
from concurrent.futures import Future
def call_with_future(fn, future, args, kwargs):
try:
result = fn(*args, **kwargs)
future.set_result(result)
except Exception as exc:
future.set_exception(exc)
def threaded(fn):
def wrapper(*args, **kwargs):
future = Future()
Thread(target=call_with_future, args=(fn, future, args, kwargs)).start()
return future
return wrapper
class MyClass:
@threaded
def get_my_value(self):
return 1
>>> my_obj = MyClass()
>>> fut = my_obj.get_my_value() # this will run in a separate thread
>>> fut.result() # will block until result is computed
1
Si vous n'avez pas classe concurrent.futures.Future (parce que, par exemple, vous utilisez Python2.7 ou une version antérieure), vous pouvez utiliser cette implémentation simplifiée:
from threading import Event
class Future(object):
def __init__(self):
self._ev = Event()
def set_result(self, result):
self._result = result
self._ev.set()
def set_exception(self, exc):
self._exc = exc
self._ev.set()
def result(self):
self._ev.wait()
if hasattr(self, '_exc'):
raise self._exc
return self._result
Je conseille de lire le module concurrent.futures car il a beaucoup d'outils soignés. Par exemple, la classe Thread
doit être remplacée par une instance ThreadPoolExecutor
pour limiter la concurrence (par exemple, vous ne voulez pas spammer des threads 10k). De plus, avec ThreadPoolExecutor
, le code est encore plus simple (et moins sujet aux erreurs):
from concurrent.futures import ThreadPoolExecutor
tp = ThreadPoolExecutor(10) # max 10 threads
def threaded(fn):
def wrapper(*args, **kwargs):
return tp.submit(fn, *args, **kwargs) # returns Future object
return wrapper
N'oubliez pas que vous devez tp.shutdown()
après avoir terminé tout le travail en parallèle.
Vous pouvez passer l'instance de classe au thread:
class SomeThread(threading.Thread):
def __init__(self, count, instance):
threading.Thread.__init__(self)
self.instance = instance
def run(self):
print "Do something"
self.instance.some_param = data
self.instance.some_function()