Правильное использование QEventLoop

У меня есть сомнения, как мне использовать QEventLoop. У меня есть 2 фрагмента кода, оба они у меня работают (загрузить веб-ресурс).

Первый:

QNetworkAccessManager *manager = new QNetworkAccessManager( this );
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "Mozilla Firefox");
connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
manager->get( request )  ;

QEventLoop loop;
connect(manager, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));
loop.exec();

Второй:

QNetworkAccessManager *manager = new QNetworkAccessManager( this );
QNetworkRequest request;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "Mozilla Firefox");
manager->get( request )  ;

QEventLoop loop;
connect(manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(replyFinished(QNetworkReply*)));
loop.exec();

Я хочу знать, какой из них я должен использовать. Я имею в виду, завершается ли цикл событий во втором после того, как сигнал испускается? Или мне нужно звонить quit() как в первом? Я где-то нашел второе решение, но оно мне не показалось правильным, поэтому я изменил его в первую часть кода.


person Bartek Boczar    schedule 04.04.2015    source источник
comment
Как вы хотите прервать цикл событий во втором случае? Сначала все в порядке, но вы также должны обрабатывать ошибки.   -  person Dmitry Sazonov    schedule 04.04.2015
comment
Да, это то, о чем я беспокоился, поэтому я изменил его. Я просто не был уверен, правильно ли я думаю, поэтому я спросил   -  person Bartek Boczar    schedule 04.04.2015
comment
Как правило, вы не должны использовать ни то, ни другое — QApplication уже устанавливает цикл обработки событий для основного потока, а QThread устанавливает цикл обработки событий для фоновых потоков.   -  person MrEricSir    schedule 04.04.2015
comment
Если вы добавите дополнительное соединение для завершения цикла событий во втором случае, то оно будет таким же, как и в первом случае. О чем ваш вопрос?   -  person Dmitry Sazonov    schedule 04.04.2015
comment
@MrEricSir ты ошибаешься. QEventLoop предназначен для таких случаев. Когда вы не хотите делать свой код сложным (с большим количеством сигналов/слотов) и вам нужен единый поток с поддержкой логики, управляемой событиями.   -  person Dmitry Sazonov    schedule 04.04.2015
comment
Локальные циклы QEventLoop — корень всех зол. (поскольку до возврата loop.exec() может произойти все что угодно). Подключите готовое к другому слоту и продолжите там.   -  person Frank Osterfeld    schedule 04.04.2015
comment
Я согласен с Фрэнком, использование цикла событий для этого случая кажется чрезмерным и может создать сложную проблему.   -  person Damien    schedule 21.09.2016


Ответы (2)


Я согласен с @Mher-Didaryan в том, что цикл событий, запущенный следующей строкой кода loop.exec(); во втором фрагменте кода, никогда не завершится. Это связано с тем, что connect() между SIGNAL и SLOT выполняется для другого цикла событий, отличного от цикла событий, указанного с помощью EventLoop loop; .

В случае 1-го фрагмента кода логика зависит от сигнала finished(QNetworkReply*), связанного с одним и тем же запросом GET, отправляемым в два разных цикла обработки событий. Но вполне возможно, что

    connect(manager, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));

вполне может выполниться после того, как manager->get( request ) ; выдаст сигнал finished(QNetworkReply*). Возможно, это может произойти для операции HTTP типа GET, включающей очень маленький файл или ответ. В таком сценарии цикл событий, начатый loop.exec(); в 1-м фрагменте кода, также не завершится. Я думаю, это то, что @Mher-Didaryan также спрашивает в своем ответе.

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

  1. Истечение времени запроса GET (скажем, из-за проблем с сетевым подключением)
  2. Ответ типа ошибки со стороны сервера сети

    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QNetworkRequest request;
    QEventLoop loop;
    QTimer getTimer; // let's use a 10 second period for timing out the GET opn
    request.setUrl(QUrl(url));
    request.setRawHeader("User-Agent", "Mozilla Firefox");
    // connect the timeout() signal of getTimer object to quit() slot of event loop
    QTimer::connect(&getTimer,SIGNAL(timeout()),&loop, SLOT(quit()));
    QObject::connect(manager, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));
    QNetworkReply *resp = manager->get( request );        
    getTimer.start(10000); // 10000 milliSeconds wait period for get() method to work properly
    loop.exec();
    
    if(NULL == resp)
    {
        // Error. we probably timed out i.e SIGNAL(finished()) did not happen
        // this handles above indicated case (1)
        return -1; // or return some timeout related error value
    }
    else if( QNetworkReply::NoError != resp->error() )
    {
        // Error - SIGNAL(finished()) was raised but get() opn failed & returned with error
        // Refer http://doc.qt.io/qt-4.8/qnetworkreply.html#NetworkError-enum
        // This section of code handles above indicated case (2)
    }
    else
    {
        // get() operation was Successful !.
        // read the response available in the 'resp' variable as a QString & parse it. 
        // Obtain the necessary result and etc.
    }
    
    delete resp;
    delete manager;
    
person Experienced_Learner    schedule 24.07.2017
comment
QNetworkAccessManager не будет удален в первый раз, если это правда. - person hfrmobile; 04.11.2020
comment
QWaitCondition: возможно уничтожение, пока потоки все еще ожидают - person hfrmobile; 04.11.2020

В вашем втором примере цикл событий никогда не завершится, с другой стороны, в вашем первом примере цикл завершится, когда finished(QNetworkReply*) испустится. Но что, если manager->get( request ); вызовет генерацию сигнала finished(QNetworkReply*) до того, как вы подключите к нему выход из цикла?

QNetworkAccessManager *manager = new QNetworkAccessManager( this );
QNetworkRequest request;
QEventLoop loop;
request.setUrl(QUrl(url));
request.setRawHeader("User-Agent", "Mozilla Firefox");
connect(manager, SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
connect(manager, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit()));
manager->get( request )  ;

loop.exec();

А также нужно как-то обработать ситуацию, когда менеджер вообще не выдает SIGNAL(finished(QNetworkReply*)).

person Mher Didaryan    schedule 01.01.2017