C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > Qt QtConcurrent::run异步等待和同步调用

Qt使用QtConcurrent::run实现异步等待和同步调用

作者:__ocean

Qt使用QtConcurrent::run实现C#中类似async await的用法,虽不如C#的async/await灵活,但可满足业务需求并提升代码可读性,下面就来详细的介绍一下QtConcurrent::run实现异步等待和同步调用

在使用Qt进行开发时,经常需要使用异步方法,不同于C#的async/await,Qt中提供了QtConcurrent::run接口方法可供调用,习惯了C#的await,便想着能不能封装几个类似的函数在项目中使用,探索了下,有如下几个方案

首先定义全局线程池

	inline QThreadPool* globalThreadPool() 
	{
		static QThreadPool* pool = []() {
			QThreadPool* p = new QThreadPool;
			p->setMaxThreadCount(QThread::idealThreadCount());
			return p;
			}();
		return pool;
	}

方案一,最简单的封装调用,直接异步调用,无任何返回结果,也不会卡住调用线程:

	auto CallAsync = [](auto func){
		QtConcurrent::run(globalThreadPool(), func);
	};

调用时用法如下:

CallAsync([](){
	// do something in theadpool...
});

方案二,如果我们想在异步执行时,调用线程同步等待,可封装如下:

auto AwaitCallAsync = [](auto func, int timeoutSeconds = 5) -> bool
{        
    QFuture<void> future = QtConcurrent::run(globalThreadPool(), func);
    
    // 使用智能指针管理对象生命周期
    auto watcher = std::make_shared<QFutureWatcher<void>>();
    auto loop = std::make_shared<QEventLoop>();
    auto timer = std::make_shared<QTimer>();
    
    bool timedOut = false;    
    if (timeoutSeconds > 0) {
        timer->setInterval(timeoutSeconds * 1000);
        timer->setSingleShot(true);
        QObject::connect(timer.get(), &QTimer::timeout, [&timedOut, loop]() {
            timedOut = true;
            loop->quit();
        });
        timer->start();
    }    
    QObject::connect(watcher.get(), &QFutureWatcher<void>::finished, loop.get(), &QEventLoop::quit);
    watcher->setFuture(future);    
    loop->exec();
    
    // 清理资源
    if (!timedOut && timeoutSeconds > 0) {
        timer->stop();
    }    
    return !timedOut; // 返回是否正常完成
};

此时,执行AwaitCallAsync时,调用线程会同步等待但并不会卡住线程,为了避免长时间等待,也可以添加超时参数。

方案三,有时,我们在希望在异步函数调用完成后能回到调用线程继续执行,那么可以添加QFutureWatcher,监控异步函数的执行,然后在QFutureWatcher发送finished时执行另一个函数,如下:

auto CallAsyncWithCallback = [](auto func_async, auto func_callback){
		auto future = QtConcurrent::run(globalThreadPool(), func_async);
		auto watcher = new QFutureWatcher<void>();
		// 连接信号,此处connect会被自动执行为Qt::QueuedConnection
		QObject::connect(watcher, &QFutureWatcher<void>::finished, [func_callback, watcher]() mutable { 
				func_callback();
				watcher->deleteLater(); // 完成后自动清理
			});
		watcher->setFuture(future);
	}

上面的connect是在调用线程中执行的,而finished信号是在线程池中子线程中发出来的,跨线程所以Qt会选择用Qt::QueuedConnection的方式执行Lambda 表达式。

方案四,有时,我们希望回调函数在特定线程比如主线程中执行,如下:

auto CallAsyncWithUICallback = [](FuncAsync func_async, FuncCallback func_callback_onUI) {
		QtConcurrent::run([func_async, func_callback]() {			
			func_async(); // 在子线程执行异步函数
			
			// 回到主线程执行回调
			QMetaObject::invokeMethod(qApp, [func_callback]() {
				func_callback();
				}, Qt::QueuedConnection);
			});
	}

注意,在调用invokeMethod时,要显示指定Qt::QueuedConnection。

总体来说,C#的async await很灵活很强大,Qt虽然不能与之相比,但经过简单的封装,也能写出比较灵活或者符合自己业务需求而又简洁好读的异步代码。

到此这篇关于Qt使用QtConcurrent::run实现异步等待和同步调用的文章就介绍到这了,更多相关Qt QtConcurrent::run异步等待和同步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文