QEventLoop的正确使用方法

9

我有疑问如何使用 QEventLoop。我有两段代码,它们都对我有效(获取网络资源下载)。

第一段:

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()?我在某处找到了第二个解决方案,但对我来说似乎不合适,所以我将其修改为第一个代码片段。

在第二种情况下,您想如何中断事件循环?第一种方式是可以的,但您也应该处理错误。 - Dmitry Sazonov
2
一般情况下,你不应该使用它们中的任何一个——QApplication已经为主线程设置了事件循环,而QThread则为后台线程设置了事件循环。 - MrEricSir
1
@MrEricSir,你说错了。 QEventLoop 就是为这种情况设计的。当您不想让代码复杂化(使用大量信号/槽)并需要支持事件驱动逻辑的单个流时,可以使用它。 - Dmitry Sazonov
5
本地 QEventLoops 是所有问题的根源。(因为在 loop.exec() 返回之前可能发生各种事情)。将 finished 信号连接到另一个槽中,并在那里继续执行。 - Frank Osterfeld
1
我同意Frank的观点,对于这种情况使用事件循环似乎有些过头了,可能会引起复杂的问题。 - Damien
显示剩余2条评论
2个回答

4
我同意 @Mher-Didaryan 的观点——在第二个代码片段中,由代码 loop.exec(); 启动的事件循环将永远不会退出。这是因为 SIGNAL 和 SLOT 之间的 connect() 是针对与通过 EventLoop loop; 指定的事件循环不同的另一个事件循环进行的。

对于第一个代码片段,逻辑取决于与同一 GET 请求相关联的 finished(QNetworkReply*) 信号被发射到两个不同的事件循环上。但很可能会出现这样的情况:

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

manager->get( request ) ; 发出 finished(QNetworkReply*) 信号之后,可能会执行。也许这种情况会发生在涉及非常小的文件或响应的 GET 类型 HTTP 操作中。在这种情况下,第一个代码片段中由 loop.exec(); 启动的事件循环也不会退出。我猜这也是 @Mher-Didaryan 在他的答案中询问的问题。
也许您可以使用以下 QEventLoop 逻辑,它将处理以下负面执行场景。
  1. Timing out of the GET request (say due to network connectivity issues)
  2. Error type response from server side of network

    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;
    

1
当第一个if为真时,QNetworkAccessManager不会被删除。 - hfrmobile
QWaitCondition:在线程仍在等待时被销毁是可能的。 - hfrmobile

1
在您的第二个示例中,事件循环将永远不会退出,另一方面,在您的第一个示例中,当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*)) 的情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接