Russian Qt Forum
Ноябрь 22, 2024, 16:46 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Вопрос ламера о реализации многопоточности  (Прочитано 23150 раз)
Hvzh
Гость
« : Декабрь 01, 2016, 00:41 »

Доброго времени суток!

По роду своей деятельности я практически не сталкивался с разработкой многопоточных приложений, но теперь пришлось. Задача следующая. Пишу программу, которая занимается обработкой видеопотока в режиме реального времени. Первая часть программы представляет собой Web-сервер (за основу взял QtWebApp), вторая - модуль обработки видеопотока. Удаленный клиет шлет на Веб-сервер запросы типа начать обработку видеопотока, закончить обработку видеопотока, получить текущие данные по обработке. Я хотел сделать так, чтобы при получении запроса на начало обработки запускался бы модуль обработки в отдельном потоке и работал бы до получения сигнала об окончании или при окончании видеопотока. Не могу понять, как это можно реализовать по уму.
Записан
qate
Супер
******
Offline Offline

Сообщений: 1177


Просмотр профиля
« Ответ #1 : Декабрь 01, 2016, 09:10 »

почитать для ознакомления https://wiki.qt.io/Threads_Events_QObjects, можно еще "Энтони Уильямс - Параллельное программирование на С++ в действии"

пришли данные на обработку - кинуть их событием в поток
пришел сброс - кинуть событие в поток
поток завершил работу - событие об окончании

Записан
Hvzh
Гость
« Ответ #2 : Декабрь 09, 2016, 11:38 »

В общем, где-то я с потоками таки накосячил. Как у меня сейчас все устроено. Запуск http-сервера:

Код:
int main(int argc, char *argv[])
{
//    qInstallMessageHandler(myMessageOutput);

    QCoreApplication app(argc, argv);

    // Load the configuration file
    QString configFileName=searchConfigFile();
    QSettings* listenerSettings=new QSettings(configFileName, QSettings::IniFormat, &app);

    listenerSettings->beginGroup("listener");

    // Start the HTTP server
    new HttpListener(listenerSettings, new RequestMapper(&app), &app);
    return app.exec();
}

Вот код маппера запросов:
Код:
void RequestMapper::service(HttpRequest& request, HttpResponse& response) {
    QByteArray path=request.getPath();
    qDebug("RequestMapper: path=%s",path.data());

    if (path=="/stop")
    {
        StopController().service(request, response);
    }
    else if (path=="/start")
    {
        StartController().service(request, response);
    }
    else if(path=="/data")
    {
        DataController().service(request, response);
    }
    else
    {
        response.setStatus(404,"Not found");
        response.write("The URL is wrong, no such document.",true);
    }

    qDebug("RequestMapper: finished request");
}
Сервер получает запрос start на присоединение к видеопотоку. Я делаю следующее:
Код:
void StartController::service(HttpRequest &request, HttpResponse &response) {
    QJsonObject json;

    cameraID = 0;
    newCameraID = 0;

    QByteArray url = request.getParameter("url");
    QByteArray minArea = request.getParameter("minArea");
    QByteArray maxArea = request.getParameter("maxArea");
    QByteArray startx = request.getParameter("x0");
    QByteArray starty = request.getParameter("y0");
    QByteArray endx = request.getParameter("x1");
    QByteArray endy = request.getParameter("y1");

    dbthread = new QThread;
    impthread = new QThread;

    worker = new DbWriteWorker();
    worker->setInterval(60);
    worker->moveToThread(dbthread);

    impworker = new ImageProcessingWorker(0, QString::fromUtf8(startx).toInt(), QString::fromUtf8(starty).toInt(),
                                          QString::fromUtf8(endx).toInt(), QString::fromUtf8(endy).toInt(),
                                          QString::fromUtf8(minArea).toInt(), QString::fromUtf8(maxArea).toInt(), 1, QString::fromUtf8(url));
    impworker->moveToThread(impthread);
    // Соединяем сигнал started потока, со слотом process "рабочего" класса, т.е. начинается выполнение нужной работы.
    connect(dbthread, SIGNAL(started()), worker, SLOT(process()));
    connect(impthread, SIGNAL(started()), impworker, SLOT(process()));

    // По завершению выходим из потока, и удаляем рабочий класс
    connect(worker, SIGNAL(finished()), dbthread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(impworker, SIGNAL(finished()), impthread, SLOT(quit()));
    connect(impworker, SIGNAL(finished()), impworker, SLOT(deleteLater()));

    // Удаляем поток, после выполнения операции
    connect(dbthread, SIGNAL(finished()), dbthread, SLOT(deleteLater()));
    connect(impthread, SIGNAL(finished()), impthread, SLOT(deleteLater()));
    connect(impworker, SIGNAL(sendCounters(int,int, int)), worker, SLOT(waitForDB(int,int,int)));

    connect(worker, SIGNAL(getDataSignal()), impworker, SLOT(prepareCounters()));

    dbthread->start();
    impthread->start();

Оба потока нормально стартуют и работают, НО возникает ОЧЕНЬ большая проблема. Когда worker посылает сигнал getDataSignal, то impworker его не ловит и, соответственно, не может переслать данные в worker. Я подозреваю, что это связано с тем, что потоки создаются внутри StartController::service(HttpRequest &request, HttpResponse &response), но как выйти из этого положения я не представляю...
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #3 : Декабрь 09, 2016, 11:44 »

В дебаг ничего не выдает? А impworker чем занят все время? Может, он просто не дает прокрутиться очереди событий?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #4 : Декабрь 09, 2016, 11:46 »

В дебаг ничего не выдает? А impworker чем занят все время? Может, он просто не дает прокрутиться очереди событий?
Об этом я не подумал... impworker в это время занимается в цикле чтением и обработкой видеопотока. Может, он и тормозит очередь. А можно ли это как-то подталкивать периодически?
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #5 : Декабрь 09, 2016, 11:53 »

Наверное, у него просто некорректное чтение или обработка. Если читаются данные из файла через сигнал readyRead, то сигналы будут приходить. Надо код посмотреть, чтобы понять.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #6 : Декабрь 09, 2016, 11:55 »

Вот основной цикл impworker:

Код:
    while(1)
    {

        isRead = capture.read(imgFrame2);

        blobProcess(imgFrame1, imgFrame2, frameCount);

        imgFrame2Copy = imgFrame2.clone();          // get another copy of frame 2 since we changed the previous frame 2 copy in the processing above

        drawBlobInfoOnImage(blobs, imgFrame2Copy);

        bool blnAtLeastOneBlobCrossedTheLine = checkIfBlobsCrossedTheLine(blobs);

        if (blnAtLeastOneBlobCrossedTheLine == true) {
            cv::line(imgFrame2Copy, crossingLine[0], crossingLine[1], SCALAR_GREEN, 2);
        }
        else {
            cv::line(imgFrame2Copy, crossingLine[0], crossingLine[1], SCALAR_RED, 2);
        }

        drawCarCountOnImage(countAB, countBA, imgFrame2Copy);

        if(isDebug)
            cv::imshow("imgFrame2Copy", imgFrame2Copy);
        writer.write(imgFrame2Copy);

        imgFrame1 = imgFrame2.clone();           // move frame 1 up to where frame 2 is

        capture.read(imgFrame2);

        blobsPrev.clear();

        for(int i = 0; i < blobs.size(); i++)
          blobsPrev.push_back(blobs[i]);


        blnFirstFrame = false;
        frameCount++;
        if(procToKill == cameraID)
            break;
        QThread::msleep(80);
    }

capture читает фреймы из rtmp-потока.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #7 : Декабрь 09, 2016, 11:57 »

Собственно, как я и предполагал - очередь событий тут не проталкивается. Почему бы тебе из этого класса не кидать сигнал с картинкой?
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #8 : Декабрь 09, 2016, 12:01 »

Собственно, как я и предполагал - очередь событий тут не проталкивается. Почему бы тебе из этого класса не кидать сигнал с картинкой?
Ты имеешь ввиду не писать тут в файл, а в каком-нибудь другом потоке?
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #9 : Декабрь 09, 2016, 12:04 »

Нет, я имею ввиду не кидать сигнал getDataSignal из вокера, пусть вокер ждет сигналов от impworker
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #10 : Декабрь 09, 2016, 12:06 »

Нет, я имею ввиду не кидать сигнал getDataSignal из вокера, пусть вокер ждет сигналов от impworker
В принципе, можно попробовать по таймеру кидать... Сейчас попробую...
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #11 : Декабрь 09, 2016, 12:10 »

Зачем по таймеру? Получил/обработал/кинул.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #12 : Декабрь 09, 2016, 12:26 »

Зачем по таймеру? Получил/обработал/кинул.
Там какая фигня. На изображении подсчитываются объекты, количество которых Worker записывает в базу данных. Если посылать сигнал после каждого найденного объекта, то тормоза будут. Но, тем не менее, с таймером тоже не покатило. Сделал так:

Код:
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(prepareCounters()));
    timer->start(5000);

Код слота:

Код:
void ImageProcessingWorker::prepareCounters()
{
    qDebug() << "ImageProcessingWorker::prepareCounters send counters";
    emit sendCounters(cameraID, countAB, countBA);
}

В итоге worker получает все накопившиеся сигналы только после остановки потока impworker
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #13 : Декабрь 09, 2016, 12:35 »

Опять ты неверно мыслишь. Улыбающийся У тебя таймер не будет срабатывать, ибо нет обработки очереди сообщений. Тебе нужно явно кидать сигнал внутри while.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hvzh
Гость
« Ответ #14 : Декабрь 09, 2016, 12:44 »

Опять ты неверно мыслишь. Улыбающийся У тебя таймер не будет срабатывать, ибо нет обработки очереди сообщений. Тебе нужно явно кидать сигнал внутри while.
QCoreApplication::instance()->processEvents() внутри while спасло отца русской демократии  Смеющийся
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.198 секунд. Запросов: 23.