Как использовать многопоточность для вызова http API в Python - PullRequest
0 голосов
/ 12 марта 2020

У меня есть проект, в котором я отображаю прямую трансляцию видео с камеры USB с помощью opencv. Между кодом есть условие. Когда он становится True, мне нужно вызвать API, чтобы получить некоторые данные, которые я отображаю в фрейме живого видео. Вызов API и получение ответа занимает некоторое время (2-3se c), в течение которого кадр замораживается, что выглядит не очень хорошо. Ниже приведен фрагмент кода:

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    cv2.putText(frame, txt, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)

    cv2.imshow(win_name, frame)

    key = cv2.waitKey(1)
    if key == ord('q'):
        break

В приведенном выше коде, когда условие if становится True, код вызывает API, и ответ rdata занимает некоторое время, из-за чего приложение на некоторое время зависает , Как я могу эффективно обрабатывать вызов и получать ответ от API в другом потоке, и как только он получает результат, он отображается на frame. Я не очень опытен в threads. Пожалуйста помоги. Спасибо

Ответы [ 2 ]

2 голосов
/ 21 марта 2020

Я полагаю, что вид логики c, который вы ищете, выглядит примерно так:

1) запустить поток для запроса api

2), пока запрос не завершен, выполните другие действия. задача (показать фрейм без данных API)

3) прервать, когда закончите, (показать фрейм с данными API)

Я считаю, что ключом для вашего ответа является использование thread.is_alive, которое проверяет, поток активен и возвращает логическое значение в зависимости от состояния потока. Метод is_alive () часто используется в синхронизации потоков.

Я бы сделал то, что вы ищете, выполнив что-то похожее на этот фиктивный код:

import sys
import trace
import threading
import time
def show_frame(subject=""):
    if subject:
        print(f"showing frame with {subject}")
    else:
        print(f"showing frame")

def request_api():
    time.sleep(5)
    return "data from api"

def show_frame_from_api():
    subject_dataAPI=request_api()
    show_frame(subject=subject_dataAPI)

t = threading.Thread(target=show_frame_from_api, args=())
t.start()

while t.is_alive():
    show_frame()

Применяя его в вашем примере, Я бы предложил что-то вроде этого:

import sys
import trace
import threading
import time
def show_frame(win_name, frame, apiText=""):
    if apiText:
        text=f'display with {apiText}'
        cv2.putText(frame, text, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
        cv2.imshow(win_name, frame)
    else
        text=f'display without api text :('
        cv2.putText(frame, text, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)
        cv2.imshow(win_name, frame)

def show_frame_from_api(win_name, frame):
    subject_dataAPI=request_api()
    show_frame(win_name, frame, apiText=subject_dataAPI)


def request_api():
    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    return txt

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    t = threading.Thread(target=show_frame_from_api, args=(win_name, frame))
    t.start()

    while t.is_alive():
        show_frame(win_name, frame)


    key = cv2.waitKey(1)
    if key == ord('q'):
        break
0 голосов
/ 17 марта 2020

Библиотека потоков в Python довольно проста в использовании и хорошо документирована .

Первое, что вам нужно сделать, - это обернуть код, который вы хотите запустить независимо. в качестве функции вы можете использовать библиотеку для запуска этой функции в отдельном потоке, как показано ниже:

import threading

thread = threading.Thread(target=yourfunction, args=('arg1', 'arg2'))
thread.start()

Обратите внимание, что получить значение результата из выполненной функции немного сложнее , поэтому я бы посоветовал вам запустить весь блок (т.е. вызов API + отображение кадра) в отдельном потоке. Ваш код должен выглядеть следующим образом:

def show_frame_from_api(win_name, frame):
    conn = http.client.HTTPSConnection('api.tive.com')
    conn.request("POST", "/get_status", json_data, headers)
    response = conn.getresponse()
    rdata = response.read()
    rdata = rdata.decode('utf8')
    rdata = json.loads(rdata)

    subject = rdata['subject']

    txt = "SUBJECT: {}".format(subject)
    cv2.putText(frame, txt, (5, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1, (0, 0, 255), 1)

    cv2.imshow(win_name, frame)

if config['ROIX1'] < f_startX and config['ROIX2']:
    # Call the API and get the response
    """
    OTHER CODE
    """

    t = threading.Thread(target=show_frame_from_api, args=(win_frame, frame))
    t.start()

    key = cv2.waitKey(1)
    if key == ord('q'):
        break

Включение ли вы блока OTHER CODE и блока cv2.waitKey в функцию зависит от того, чего вы пытаетесь достичь, но вы должны понять .

Обратите внимание, что вам не следует вызывать t.join(), чтобы дождаться окончания потока sh, поскольку это также приведет к зависанию вашего основного потока. Как я уже упоминал, самый простой подход - просто передать потоку все действия, относящиеся к вызову API, и обработку ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...