1 Востаннє редагувалося ADR (23.02.2017 15:44:45)

Тема: Як гарніше це перенести в потік?

Параметри:

вхідний_файл = 'мій файл.тхт'
вихідна_папка = 'папка'

виконати_функцію_б = так/ні
виконати_функцію_в = так/ні

параметри_для_функції_б = 123
параметри_для_функції_в = 123

Функції:

результат_а = функція_а(вхідний_файл)

якщо виконати_функцію_б то:
    результат_б1, результат_б2 = функція_б(результат_а, параметри_для_функції_б)
    зберегти_на_диск(вихідна_папка, результат_б1)
    зберегти_на_диск(вихідна_папка, результат_б2)
    
    результат_а = результат_б1
    
якщо виконати_функцію_в то:
    результат_в = функція_в(результат_а, параметри_для_функції_в)
    зберегти_на_диск(вихідна_папка, результат_б2)

Суть в тому, щоб не виста GUIшка. Ну так, можна то все запхати в одну функцію, яка буде приймати кучу параметрів, але якось то мені не дуже подобається.

Який є більше елегантний підхід?

2

Re: Як гарніше це перенести в потік?

Я би запхнув всі параметри в структуру з гарною назвою типу "thread_context" і передавав її

Maybe a = Just a | Nothing
Подякували: Torbins, DOP2

3

Re: Як гарніше це перенести в потік?

ADR
Якщо параметри однакові, і алгоритми також, то можна посилання на функцію "б" або "в" передавати. Типу такого:

TThread.Execute(@FunctionB, ParamB);

Можна й масивом по кілька посилань.

4

Re: Як гарніше це перенести в потік?

Два варіанти, які, зрештою, зводяться до одного: або, як сказав 0x9111A, створити об'єкт з параметрів і передавати параметром один об'єкт; або ж створити об'єкт, який буде інкапсулювати всю діяльність цього потоку, виставляти його параметри і викликати його метод run, який вже створюватиме потік.

5

Re: Як гарніше це перенести в потік?

0x9111A написав:

Я би запхнув всі параметри в структуру з гарною назвою типу "thread_context" і передавав її

Це мало чим відрізняється від просто кучки параметрів)
Робити структуру, яка виконається рівно один раз... хм... може то в поля класу тоді запхати. (бо це насправді були методи, а не функції, але це не багато змінює)

Torbins написав:

ADR
Якщо параметри однакові, і алгоритми також, то можна посилання на функцію "б" або "в" передавати. Типу такого:

TThread.Execute(@FunctionB, ParamB);

Можна й масивом по кілька посилань.

Проблема в тому, що вони з'єднані цепочкою, при чому не всі ланки цієї цепочки обов'язкові.

koala написав:

Два варіанти, які, зрештою, зводяться до одного: або, як сказав 0x9111A, створити об'єкт з параметрів і передавати параметром один об'єкт; або ж створити об'єкт, який буде інкапсулювати всю діяльність цього потоку, виставляти його параметри і викликати його метод run, який вже створюватиме потік.

Параметри передаються з форми — все одно вийде, що буде фукнція, яка то все приймає...

Зараз воно виглядає так:

class RawDataParser(AbstractWorker):
    ...

    def start(
            self,
            input_file: str,
            output_path_pattern: str,
            check_data_exists: bool,
            sel_chip_list: List[str],
            sel_temp_list: List[str],
            make_vertical_and_horizontal_tables: bool = True,
            custom_parameters_count: int = 0,
            check_data_correct: bool = False,
            columns: List[int] = (),
            coverage_factor: float = 10
    ):
        self.__thread = threading.Thread(target=self.__parse,
                                         args=(input_file, output_path_pattern, check_data_exists,
                                               sel_chip_list, sel_temp_list, make_vertical_and_horizontal_tables,
                                               custom_parameters_count, check_data_correct, columns, coverage_factor))
        self.__thread.start()

    def __parse(self, input_file: str, output_path_pattern: str, check_data_exists: bool,
                sel_chip_list: List[str], sel_temp_list: List[str], make_vertical_and_horizontal_tables: bool,
                custom_parameters_count: int, check_data_correct: bool, columns: List[int], coverage_factor: float):
        ...

Робив нову версію і думав за одно зробити гарніше.

Подякували: 221VOLT1

6

Re: Як гарніше це перенести в потік?

А покажіть іще, як ви цей RawDataParser.start() викликаєте.

7 Востаннє редагувалося Torbins (23.02.2017 20:47:44)

Re: Як гарніше це перенести в потік?

ADR написав:
Torbins написав:

Якщо параметри однакові, і алгоритми також, то можна посилання на функцію "б" або "в" передавати. Типу такого:

TThread.Execute(@FunctionB, ParamB);

Можна й масивом по кілька посилань.

Проблема в тому, що вони з'єднані цепочкою, при чому не всі ланки цієї цепочки обов'язкові.

Важливо лише те, що у них спільного. Якщо спільне лише те, що вони виконуються послідовно, то доведеться робити клас/інтерфейс, з яким буде працювати потік, обробляти нащадків цього класу (імплементаторів інтерфейсу) по черзі. А потім робити нащадків з потрібними параметрами.
У будь якому випадку параметри для різних функцій треба тримати окремо.

8 Востаннє редагувалося ADR (23.02.2017 22:00:19)

Re: Як гарніше це перенести в потік?

koala написав:

А покажіть іще, як ви цей RawDataParser.start() викликаєте.

Не думаю, що це сильно допоможе:

    def _parser_run_button_clicked(self):
        input_file_path = os.path.join(self.paths.measurement_result_folder, self.parserInputFileCBox.currentText())
        split_file_name = self.parserInputFileCBox.currentText().split('.')
        output_folder = os.path.join(self.paths.measurement_result_folder, PARSED_DATA_FOLDER)
        if not os.path.isdir(output_folder):
            os.mkdir(output_folder)

        output_path_pattern = os.path.join(output_folder, '.'.join(split_file_name[:-1]) + '_{}.' + split_file_name[-1])

        sel_chip_list = QListWidgetHelper.get_sel_items(self.parserChipsLstWgt)
        sel_temp_list = QListWidgetHelper.get_sel_items(self.parserTempLstWgt)

        parser = RawDataParser()
        self.general_signal_connector(parser)
        parser.find_incorrect_data_done.connect(self._incorrect_data_found)
        parser.start(
            input_file=input_file_path,
            output_path_pattern=output_path_pattern,
            check_data_exists=self.parserCheckDataExistsCBox.isChecked(),
            sel_chip_list=sel_chip_list,
            sel_temp_list=sel_temp_list,
            make_vertical_and_horizontal_tables=self.makeHVTablesCBox.isChecked(),
            custom_parameters_count=self.parserCustomParametersCountSBox.value(),
            check_data_correct=self.parserCheckDataValueCBox.isChecked(),
            columns=[i + self.parser_start_data_index
                     for i in QListWidgetHelper.get_sel_indexes(self.parserIncorrectFindColLstWgt)],
            coverage_factor=self.parserCoverageFactorSBox.value()
        )

Torbins написав:
ADR написав:
Torbins написав:

Якщо параметри однакові, і алгоритми також, то можна посилання на функцію "б" або "в" передавати. Типу такого:

TThread.Execute(@FunctionB, ParamB);

Можна й масивом по кілька посилань.

Проблема в тому, що вони з'єднані цепочкою, при чому не всі ланки цієї цепочки обов'язкові.

Важливо лише те, що у них спільного. Якщо спільне лише те, що вони виконуються послідовно, то доведеться робити клас/інтерфейс, з яким буде працювати потік, обробляти нащадків цього класу (імплементаторів інтерфейсу) по черзі. А потім робити нащадків з потрібними параметрами.
У будь якому випадку параметри для різних функцій треба тримати окремо.

Функція Б бере дані від функції А. Функція В бере дані або від функції Б (якщо така була), або від А.

Можна то все заховати в об'єкті, якось так (тільки, ви казали зробити окремими класами):

обгортка.функція_а_в_потоці(вхідний_файл, вихідна_папка)
обгортка.чекати_завершення()

якщо виконати_функцію_б то:
    обгортка.функція_б_в_потоці(параметри_для_функції_б)
    обгортка.чекати_завершення()
    
    
якщо виконати_функцію_в то:
    обгортка.функція_в_в_потоці(параметри_для_функції_в)
    обгортка.чекати_завершення()
Подякували: 221VOLT, koala2

9

Re: Як гарніше це перенести в потік?

ADR написав:

Не думаю, що це сильно допоможе

Ну чому ж? Погляньте - ви передаєте першим параметром в start що? Правильно, self. А що робить start? Правильно, викликає self.__thread.start(), який смикає... вже здогадалися? self.__parse, знову ж таки, з параметром self. Отже, все що треба - загнати всі змінні у властивості RawDataParser: або через __init__:

parser = RawDataParser(
    input_file = os.path.join(self.paths.measurement_result_folder, self.parserInputFileCBox.currentText()),
    parser.output_path_pattern = os.path.join(output_folder, '.'.join(split_file_name[:-1]) + '_{}.' + split_file_name[-1]), 
    ...)
parser.exec()

або навіть вручну:

parser = RawDataParser()
parser.input_file = os.path.join(self.paths.measurement_result_folder, self.parserInputFileCBox.currentText())
parser.output_path_pattern = os.path.join(output_folder, '.'.join(split_file_name[:-1]) + '_{}.' + split_file_name[-1])
...
parser.exec()

Головна проблема, як я бачу - якщо ви спробуєте запустити два exec()-а на одному RawDataParser одночасно, можливий дедлок; для уникання доведеться додати Lock в RawDataParser і вираз with в __parse:

def __init__(self):
    ...
    self.__lock = Lock()
 def __parse(self):
    with self.lock:
        ....
Подякували: ADR1

10 Востаннє редагувалося Torbins (24.02.2017 10:25:55)

Re: Як гарніше це перенести в потік?

ADR
Я б зробив типу такого:

LoaderItem := TLoaderItem.Create;
LoaderItem.FileName := 'xxx';

BItem := TBItem.Create;
BItem.Param1 := P1;
BItem.Param2 := P2;

CItem := TCItem.Create;
CItem.CParam1 := C1;

TThread.Execute([LoaderItem, BItem, CItem]);
//...
TThread.Execute;
var
  Item: TItem;
  Buffer: string;
begin
  Buffer := '';
  for Item in Items do
  begin
    Item.Input := Buffer;
    Item.Run;
    Buffer := Item.Output;
  end;
  FinalResult := Buffer;
end;

В своїх Run TBItem і TCItem виконують різні дії, і обробляють свої параметри.

Подякували: ADR1