Как позволить одной функции дождаться завершения другой, прежде чем продолжить

#python #kivy

#python #kivy

Вопрос:

Я обращаюсь к вам, сообщество, поскольку я исчерпал все идеи о том, как заставить это работать.

В основном в одном методе я проверяю паспорт с помощью камеры и извлекаю определенные функции.

Мне нужен метод send_data_to_the_server() , который будет ждать, пока первая функция check_passport() не даст мне ожидаемый результат. Когда результат получен и есть True , следующая функция (которая подключается к серверу) может продолжаться.

Я пытался заставить ее работать time.sleep() , что оказалось очень неэффективным, поскольку она замораживает всю программу и перестает отвечать на запросы, и я должен ее убить.

Затем я попытался использовать queue.Queue с threading модулями. Это тоже зависает.

мой код:

 def check_passport(self, id, q):  # <-- this function needs to finish and return True
        self.validate_passport = NewUserValidationCamera()
        self.root.add_widget(self.validate_passport)
        if self.validate_passport._passport_passed:  # this private variable will become True at some point
            q.put((id, 'finished'))
        # while not self.validate_passport._passport_passed:
        #     time.sleep(0.5)
        # return True

def send_data_to_the_server(self):  # this method waits until method "check_passport" returns True
    ...
    ...

def register_or_login(self):
        if self.login_screen is False:
            passport_passed = False
            
            q = queue.Queue()
            thread1 = threading.Thread(target=self.check_passport, args=(1, q))
            thread2 = threading.Thread(target=self.send_data_to_the_server)
            for thread in [thread1, thread2]:
                thread.daemon = True
                thread.start()
            result = q.get()
            print(result)
  

Как я уже сказал, мое решение независимо от того, использую я time приложение или threading зависает. Я также использовал while not self.validate_passport._passport_passed: time.sleep(0.5) , но приложение также перестает отвечать на запросы.

Что вам, вероятно, также нужно знать, так это то, что self.validate_passport = NewUserValidationCamera() запускается весь процесс проверки, потому что он запускается в __init__() методе.

Обновленный код, отражающий добавленную очередь для второго метода:

 def register_or_login(self):
        if self.login_screen is False:
            passport_passed = False
            
            q1 = queue.Queue()
            q2 = queue.Queue()
            thread1 = threading.Thread(target=self.create_a_validation_widget_and_check_passport, args=(1, q1))
            thread2 = threading.Thread(target=self.send_data_to_the_server, args=(2, q2))
            for thread in [thread1, thread2]:
                thread.daemon = True
                thread.start()
                thread.join()
            result1 = q1.get()
            result2 = q2.get()
            print(result1)
  

Любая помощь очень ценится.

Комментарии:

1. Вы пробовали thread.join()?

2. Нет, я этого не делал. Попробую ..

3. Я не уверен во всей архитектуре приложения, но похоже, что вам нужен флаг файла в системе, если указанный файл существует, show может продолжаться

4. Хорошо, возможно, я здесь неправильно понимаю. Но разве метод password не может просто вызвать метод отправки данных, когда он будет готов?

5. Я еще не понял, в чем суть вопроса. Я не думаю, что это имеет какое-либо отношение к коду потока. Все это кажется простым и правильным. create_a_validation_widget_and_check_passport1 должен выполняться, и если он возвращается нормально, send_data_to_the_server затем должен выполняться. Я просто разделил ваш код на две простые функции, и это сработало как для того, чтобы просто использовать join() то, что вы делаете здесь, так и для того, чтобы отключить оба потока одновременно и заставить один ждать изменения глобальной переменной другим, прежде чем он сделает свое дело.

Ответ №1:

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

 def __init__(self):
    self.que = queue.Queue() # will use for get the data from the check_passport
    self.thread1 = None #  will use this attribute in send_data method to wait the check_passport
    self.register_or_login()

def check_passport(self, id, q):
    self.validate_passport = NewUserValidationCamera()
    self.root.add_widget(self.validate_passport)
    if self.validate_passport._passport_passed:  
        q.put((id, 'finished'))
    return True

def send_data_to_the_server(self):  
    self.thread1.join() # The method waits for the thread1
    print("check_passport: ", self.que.get())
...

def register_or_login(self):
    if self.login_screen is False:
        passport_passed = False
        
        self.thread1 = threading.Thread(target=lamda id, q: self.que.put(self.check_passport(id, q)), args=(1, q)) 
        # the lambda function puts the result of the check_passport in the self.que
        thread2 = threading.Thread(target=self.send_data_to_the_server)
        for thread in [self.thread1, thread2]:
            thread.daemon = True
            thread.start()
        result = q.get() 
        print(result)
  

Редактировать:
Прежде чем ответить на вопрос, я написал небольшой пример, и я хотел бы поделиться примером, чтобы вы могли протестировать код.

 class test:
    def __init__(self):
        self.que = queue.Queue()
        self.thread1 = threading.Thread(target=lambda arg1, arg2: self.que.put(self.func_1(arg1, arg2)), args=["hello", "world"])
        self.thread1.start()
        thread2 = threading.Thread(target=self.func_2)
        thread2.start()
    
    def func_1(self, foo_1, foo_2):
        for bar in range(10):
            print(bar)
            time.sleep(1)
    
        return "{} {}".format(foo_1, foo_2)

    def func_2(self):
        self.thread1.join()
        result = self.que.get()
        print(result)
    
  

Комментарии:

1. Вы действительно запустили этот код и увидели, что он работает? Я не нахожусь в критическом состоянии, и я не знаю, должно ли это работать или не должно. Вот почему я спрашиваю. Мне просто любопытно.

2. Не этот код, но я написал небольшой пример. Я не знаю, как добавить мой небольшой пример кода в комментарий.

Ответ №2:

Итак, в конце я сделал то, что @niclaslindgren предложил по-своему. Я называю это подходом «KISS» или также известным как «Keep It Simple Stupid». Я также принял во внимание то, что @Steve сказал об использовании глобальных переменных. Я не большой поклонник глобальных переменных, но у них есть свое место в программировании. Приведенное ниже помогло мне:

 PASSPORT_VALIDATED = False

class NewUserValidationCamera(Image):

    ...
    ...
    def update(self, dt):
        ...
        ...
        if self.names[label] == 'ma' and self._passport_keyword_passed and self._gender_check_passed:
            global PASSPORT_VALIDATED
            PASSPORT_VALIDATED = True

class MainApp(App):

    def __init__(self, **kwargs):
        super(MainApp, self).__init__(**kwargs)
        Clock.schedule_interval(self.check_for_True, 1.0)
        ...
        ...

    def check_for_True(self, dt):
        if PASSPORT_VALIDATED:
            Clock.unschedule(self.check_for_True)
            self.send_data_to_the_server()

    def send_data_to_the_server(self):  
        ...
        ...

    def register_or_login(self):
        if self.login_screen is False:
            passport_passed = False
        
            self.validate_passport = NewUserValidationCamera()
            self.root.add_widget(self.validate_passport)