Не уверен, следует ли мне использовать потоки или нет?

Я постараюсь сделать то, что я пытаюсь сделать, максимально простым.

У меня есть два класса ClassA и ClassB

ClassA имеет метод экземпляра, содержащий цикл while, который работает «бесконечно» и собирает данные. ClassA также передается экземпляр ClassB. В то время как ClassA собирает эти данные, он также проверяет поступающие данные, чтобы увидеть, был ли получен определенный сигнал. Если сигнал получен, вызывается метод экземпляра в ClassB.

Рассмотрим следующий драйвер основной программы:

from class_a import ClassA
from class_b import ClassB

database_connection = MongoDB #purely example
class_b = ClassB(database_connection)
class_a = ClassA(class_b)

А потом классы:

Class class_a:

    def __init__(self, class_b):
        self.class_b

    def collect_data(self):
        while True:
            data = receiver()
            if (signal in data):
                self.class_b.send_data_to_database(data)

Class class_b:

    def __init__(self, database):
        self.database = database

    def convert_data(self, data):
        return data + 1

    def send_data_to_database(data):
        converted_data = convert_data(data)
        self.database.send(converted_data)

Теперь вот мой вопрос. Должен ли я иметь поток для метода экземпляра send_data_to_database() в классе B? Мой мыслительный процесс заключается в том, что, возможно, создание потока только для обработки отправки данных в базу данных будет быстрее, ЧЕМ метод экземпляра НЕ является потоком. Мое мнение здесь неправильное? Мои познания в трединге ограничены. В конечном счете, я просто пытаюсь найти самый быстрый способ отправки данных в базу данных после того, как класс A распознает, что в данных есть сигнал. Спасибо всем ответившим заранее.


person Kyle DeGennaro    schedule 19.06.2019    source источник
comment
Потоки подразумевают параллелизм, то есть несколько действий одновременно. Ваш код чисто последовательный, с одним действием за другим: ... -> receive -> check -> send -> receive -> .... Выгрузка одиночного действия в поток, например. send, как правило, не стоит — запуск потока занимает больше времени, чем просто выполнение действия напрямую.   -  person MisterMiyagi    schedule 19.06.2019
comment
Что становится с собранными данными, если в данных нет сигнала? Спит ли класс А между циклами сбора данных или просто работает так быстро, как только может? Есть ли реальный риск, что он отстанет, или он может просто не торопиться, собирая данные? Что делает остальная часть приложения, кроме этой части сбора данных? Или это оно?   -  person bigh_29    schedule 19.06.2019
comment
@bigh_29 Данные, в которых нет сигнала, опускаются. ClassA не засыпает между запусками сбора данных. Чтобы не усложнять, это в значительной степени приложение (помимо обрабатываемых данных). Нет значительного риска отставания в сборе данных; Моей главной заботой является возможность отправлять данные как можно быстрее после получения этого сигнала.   -  person Kyle DeGennaro    schedule 19.06.2019
comment
@MisterMiyagi Я только что нашел это: заголовок stackoverflow.com/questions/10154487/ и быстро взглянул на нее. Может ли многопроцессорность быть лучшим (если даже необходимым) решением многопоточности?   -  person Kyle DeGennaro    schedule 19.06.2019
comment
Если нет риска отставания при сборе данных, то здесь нет необходимости в многопоточности. Конечно, не открывать поток и не закрывать его каждый раз, когда вы хотите записать в базу данных, что было бы медленнее. Если бы беспокойство заключалось в том, что сбор данных может отстать, и вы хотите, чтобы цикл while продолжался даже во время записи, тогда я бы постоянно открывал поток со вторым циклом while, контролирующим очередь (из стандартной библиотеки Python). Отправляйте запросы на запись БД в очередь по мере их поступления, и пусть второй поток обрабатывает их, пока первый поток продолжает работу.   -  person Atlas    schedule 19.06.2019
comment
@KyleDeGennaro Процессы даже дороже, чем потоки. Если вам нечего делать одновременно, делать что-то одновременно не имеет смысла. Если вы не знаете, есть ли у вас какие-либо дела одновременно, мы также не можем вам сказать. В конечном счете, параллелизм — это взвешивание затрат и выгод, а вы не определили ни того, ни другого. Сколько времени занимает конвертация? Сколько времени занимает отправка? Сколько времени занимает получение? Как долго может быть задержано получение при отправке, прежде чем это станет проблемой? Вы привязаны к процессору или вводу-выводу? И так далее...   -  person MisterMiyagi    schedule 19.06.2019
comment
@MisterMiyagi Спасибо за ваш прямой ответ; Кажется, что у меня нет параллельных задач, так как программа работает последовательно; Меня беспокоила попытка ускорить отправку данных при получении сигнала.   -  person Kyle DeGennaro    schedule 19.06.2019


Ответы (1)


Я бы использовал потоки, если одно из них верно:

  • Блокирующие вызовы базы данных ввода-вывода в B могут негативно повлиять на способность A своевременно собирать данные.
  • Эти две части сбора данных вместе могут негативно повлиять на скорость отклика других частей приложения (например, неотзывчивый графический интерфейс).

Если ни одно из условий не выполняется, однопоточное приложение доставляет гораздо меньше хлопот.

Рассмотрите возможность использования Queue для параллелизма, если вы используете потоки. Класс A может отправлять данные в очередь, которую ожидает класс B. Вот пример кода голых костей того, что я имею в виду:

from queue import Queue
from threading import Thread, Event

class class_a:
    def __init__(self, queue):
        self.queue = queue
        self.thread = Thread(target=self.collect_data)
        self.thread.start()

    def collect_data(self):
        for data in range(1000):
            if data % 3 == 0:
                print(f'Thread A sending {data} to queue')
                self.queue.put(data)
            else:
                print(f'Thread A discarding {data}')

class class_b:
    def __init__(self):
        self.queue = Queue()
        self.thread = Thread(target=self.process_data)
        self.thread.daemon = True
        self.thread.start()

    def process_data(self):
        while True:
            data = self.queue.get()
            print(f'Thread B received {data} from queue')

b = class_b()
a = class_a(b.queue)

Наконец, каждый раз, когда вы думаете об использовании параллелизма в python, вы должны спросить, есть ли многопроцессорность имеет больше смысла, чем многопоточность. Многопроцессорность — лучший выбор, когда вычислительные ресурсы ЦП, а не файловый или сетевой ввод-вывод, становятся ограничивающим фактором производительности приложения. Я не думаю, что многопроцессорность подходит для вашего проекта, основываясь на предоставленной вами информации.

person bigh_29    schedule 19.06.2019
comment
Я понимаю. Возможно, должна быть потеря времени, если реализована очередь? Потому что теперь ClassA отправляет данные в очередь, а ClassB слушает. В отличие от моего исходного примера, не добавляет ли это дополнительный шаг от получения данных от ClassA до ClassB? - person Kyle DeGennaro; 19.06.2019
comment
Я абсолютно согласен с рекомендацией использовать очередь. Таким образом, устраняются накладные расходы на запуск потока для каждого появления сигнала в данных. - person shmee; 19.06.2019
comment
Может быть, я ошибаюсь в своем объяснении; Я не планирую запускать поток при каждом появлении сигнала в данных. Просто поток для прослушивания этого сигнала, а затем отправить запрос в базу данных через HTTP; Будет ли поток метода, который отправляет HTTP-запрос, быстрее, чем нет? Или нет существенной разницы (и, возможно, пустой траты памяти), чтобы сделать это, поскольку все ДОЛЖНО происходить последовательно? - person Kyle DeGennaro; 19.06.2019
comment
@KyleDeGennaro, вставил пример кода, чтобы лучше объяснить. Класс B порождает один поток на всю жизнь приложения. Queue — отличный способ для одного потока отправлять данные другому. Возможно, вы захотите инкапсулировать очередь за вспомогательным методом в B. Зависит от того, хотите ли вы, чтобы класс A знал об экземпляре класса B или он должен был знать об общем экземпляре Q. В любом случае очередь обрабатывает связь между ними. - person bigh_29; 19.06.2019
comment
Спасибо за подробный ответ. Очень полезно! - person Kyle DeGennaro; 19.06.2019