Реализация в многопоточном стиле

Есть код, который сейчас проходится по папкам и анализирует информацию. Он работает в однопоточном стиле. Мне нужно его в многопоточном сделать. Я так понимаю, решение будет примерно таким: thread = threading.Thread(target=функция), и в конце добавлять

for ticker in tickers:
    ticker.start()
for ticker in tickers:
    ticker.join()

Но я не пойму, где именно в коде это применить, что передавать в таргет. Метод open открывает файлы по ссылке и проходится построчно по ним, метод calculate рассчитывает по формуле значения из файлов.

from utilites import show_result, generate_filenames

import threading


class Ticker(threading.Thread):

    def __init__(self, ticket_folder, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.ticket_folder = ticket_folder
        self.name_ticket = ''
        self.volatility = 0

    def run(self):
        self.calculate(self.open())

    def open(self):
        price_scope = []
        with open(self.ticket_folder, mode='r') as open_ticker:
            for element in open_ticker:
                scattered_element = element.split(',')
                self.name_ticket = scattered_element[0]
                if scattered_element[2] != 'PRICE':
                    price_scope.append(float(scattered_element[2]))
            return price_scope

    def calculate(self, unsorted):
        unsorted.sort()
        half_sum = (unsorted[0] + unsorted[-1]) / 2
        self.volatility = ((unsorted[-1] - unsorted[0]) / half_sum) * 100


def main(folder):
    zero_tickers = []
    value_key = {}
    sorted_place = []
    tickers = []


    for last_folder in generate_filenames(folder):
        tickers.append(Ticker(last_folder))

    for ticker in tickers:
        ticker.start()
    for ticker in tickers:
        ticker.join()

    for ran_ticker in tickers:
        if ran_ticker.volatility == 0:
            zero_tickers.append(ran_ticker.name_ticket)
        else:
            value_key[ran_ticker.volatility] = ran_ticker.name_ticket
            sorted_place.append(ran_ticker.volatility)
            sorted_place.sort()

    show_result(sorted_place, value_key, zero_tickers)


path = "trades/"
if __name__ == '__main__':
    main(folder=path)

Как правильно этот код реализовать в многопоточном стиле?


Ответы (2 шт):

Автор решения: Egettl

Советую создать глобальный список, например с путями к папкам. Условно назовем его mas.

Далее создать секцию для main:

if __name__ == "__main__":

Дальше можно запустить несколько потоков простым циклом:

for i in range(5):

Код, который должен исполняться следует вынести в отдельную функцию. Вы указали верный кусок кода сверху, это добавляете в цикл for.

thread = threading.Thread(target = func_name, args)

А процедуру работы программы начните с цикла while True, добавьте условие на прекращение цикла while True, если список mas оказался пустым.

Через mas.pop(0) вы можете получить первый элемент в вашем списке mas со всеми путями и папками, а также удалить этот элемент из списка mas.

Выглядеть начало вашей функции будет примерно так:

global mas
while True:
    if len(mas) == 0:
        break
    else:
        path = mas.pop(0)
        ...

Возможно существуют решения лучше. Не претендую на абсолютную правильность и эффективность.

→ Ссылка
Автор решения: eri

def run(self) это и есть target по умолчанию. Уже применен в описании класса.

Если вынести функции run, open, calculate из класса, то функцию run передавайте как target в класс Thread, а папку там в параметр функции вместо прилипания атрибутом к экземпляру через self.

Что в вашем коде вам не нравится?

Но Ваш код создает довольно много потоков, если папок много. И так как тут расчёты то лучше все переорганизовать на multiprocessing.Pool().imap_unordered(... )

→ Ссылка