Подтвердить что ты не робот

Python Threading внутри класса

Недавно я начал с модуля потоковой передачи python. После некоторых проб и ошибок мне удалось получить базовую обработку потоков, используя следующий пример кода, приведенный в большинстве руководств.

class SomeThread(threading.Thread):
    def __init__(self, count):
        threading.Thread.__init__(self)

    def run(self):
        print "Do something"

Моя проблема: у меня есть класс, у которого есть переменные класса и функция, которую я хочу запускать в отдельном потоке. Однако функция использует переменные класса, а также записывает в переменные класса. Например:

class MyClass:
    somevar = 'someval'

    def func_to_be_threaded(self):
        # Uses other class functions
        # Do something with class variables

Итак, как бы я по существу "поместил класс потока в MyClass". Итак, если MyClass(). Func_to_threaded() вызывается, он будет запускаться в потоке.

4b9b3361

Ответ 1

Если я правильно понял, вы хотите запустить функцию в отдельном потоке? Существует несколько способов сделать это. Но в основном вы завершаете свою функцию следующим образом:

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

Его можно укоротить декоратором:

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

Изменить Обновленная версия с дескриптором:

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'

Это можно использовать следующим образом:

>>> my_obj = MyClass()
>>> handle = my_obj.func_to_be_threaded()
>>> handle.join()

Теперь можно расширить его еще больше, если вы хотите вернуть значение из функции. Рассмотрим это:

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

Если у вас нет concurrent.futures.Future class (потому что, например, вы используете Python2.7 или старше), вы можете использовать эту упрощенную реализацию:

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

Я советую читать через concurrent.futures модуль, так как у него много опрятных инструментов. Например, класс Thread должен быть заменен экземпляром ThreadPoolExecutor для ограничения concurrency (например, вы не хотите спамить потоки 10k). Также с ThreadPoolExecutor код еще проще (и меньше подвержен ошибкам):

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

Просто помните, что вы должны tp.shutdown() после выполнения всей параллельной работы.

Ответ 2

Вы можете передать экземпляр класса в поток:

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

Ответ 3

Я абсолютно уверен, что вы не можете сделать ни одной функции с потоком.

Весь класс будет иметь резьбу (вроде). Когда вы создаете экземпляр объекта, его __init__ будет вызываться в другом потоке, а затем, когда вы вызываете start() на этом объекте, его run() будет вызываться один раз, в другом потоке.

Итак, если у вас есть TASK, который должен быть в своем потоке (диск IO, прослушивание сокетов и т.д.), тогда вам нужен класс для обработки этой задачи.

Ответ @ndpu решает проблемы с вашей областью/доступом.