使用QEventLoop将异步操作变成同步操作

前言

Qt事件循环详解(一)中讲到,可以用QEventLoop来阻塞当前函数并开启事件循环,在UI程序中,我们可以用这种方法将一个异步操作转化成一个同步操作。

异步调用举例

我们来看一个例子,我们向服务器请求服务器时间,异步的方式,我们的写法是这样的:

void Helper::getServerTimeAsync()
{
    auto networkManager = new QNetworkAccessManager;
    connect(networkManager, &QNetworkAccessManager::finished, [=](QNetworkReply* reply) {
        networkManager->deleteLater();
        emit serverTime(reply->rawHeader("time").toLongLong());
    });
    networkManager->get(QNetworkRequest(QUrl("https://10.10.10.1/time")));
}

在上述代码中,我们通过连接QNetworkAccessManager::finished信号来在未来某个时候发出finished信号时处理回复数据,然后将得到的时间以信号的方式发出去。
调用者在使用这个接口时,那就是下面这样:

	auto helper = new Helper;
	connent(helper, &Helper::serverTime, [](time_t time) {
    	qDebug() << time;
    });
	helper.getServerTimeAsync();

相当于调用getServerTimeAsync只是发一个获取时间的请求,这个时候并不能立即得到时间,必须在未来某个时候Helper::serverTime信号触发时才能得到时间。

改成同步调用

现在,我们通过QEventLoop来改成同步调用:

time_t Helper::getServerTimeSync()
{
    QNetworkAccessManager networkManager;
    QEventLoop eventLoop;
    QTimer timer;
    connect(&timer, &QTimer::timeout, [&eventLoop] { eventLoop.quit(); });
    connect(&networkManager, &QNetworkAccessManager::finished, [&eventLoop](QNetworkReply* reply) {
        eventLoop.quit();
    });
    auto reply = networkManager.get(QNetworkRequest(QUrl("https://10.10.10.1/time")));
    timer.start(3000);
    eventLoop.exec();
    reply->deleteLater();
    if (reply->error() != QNetworkReply::NoError)
        return 0;
    return reply->rawHeader("time").toLongLong();
}

如上代码,创建定时器用于超时处理,超时时直接退出事件循环;QNetworkAccessManager::finished触发时也退出事件循环;最后使用QEventLoop来开启事件循环阻塞当前函数(由于是阻塞调用,那networkManager不需要new,直接局部变量即可);当超时时间到或者finished信号触发时事件循环退出,我们再去从reply里解析时间,最后返回。
可能看上面代码,实现变复杂了好多,但是接口使用起来就简单很多:

qDebug() << helper.getServerTimeSync();

总结

很多异步的操作,比如上面的QNetworkAccessManager的post、get,或者使用QProcess创建子进程处理任务的情况,都可以改成同步调用,但是要注意的时,改成同步调用后,在调用返回之前,函数是被阻塞掉的,修改之前就要考虑阻塞的话会不会影响业务逻辑。

QNetworkAccessManager是Qt网络模块中的一个类,它提供了一种方便的方式来发送网络请求并接收响应。下面是对你提到的特性的详细解释: 1. 使用QEventLoop实现同步请求:QNetworkAccessManager默认是异步的,这意味着当你发送一个请求后,它会在后台运行,不会阻塞你的主线程。但是,有时你需要等到请求完成后再执行下一步操作,这时就可以使用QEventLoop实现同步请求。具体做法是,在发送请求后创建一个QEventLoop对象,然后调用其exec()方法进行阻塞等待请求完成,请求完成后再退出循环。 2. 异步请求可以传一个QVariant参数:QNetworkAccessManager的异步请求使用QNetworkReply对象来处理响应。你可以使用QNetworkRequest的setUserData()方法将一个QVariant对象附加到请求中,然后在QNetworkReply的finished()信号中获取这个对象。 3. 可以设置超时时间:QNetworkAccessManager提供了一个setTimeOut()方法,可以设置请求的超时时间,单位是毫秒。如果请求在超时时间内没有完成,则会自动取消请求并发出timeout()信号。 4. 可以使用get和post方法,并设置参数ContentTypeHeader:QNetworkAccessManager提供了get()和post()方法用于发送GET和POST请求,你可以使用QNetworkRequest的setHeader()方法设置请求头,包括Content-Type等参数。 以上就是QNetworkAccessManager的一些常用特性,希望能对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值