一、pro文件 代码:
javascript
CONFIG += c++20
msvc {
QMAKE_CFLAGS += /std:c++20 /utf-8
QMAKE_CXXFLAGS += /std:c++20 /utf-8
#QMAKE_CXXFLAGS += "/await"
#QMAKE_CXXFLAGS += /await
} else {
QMAKE_CFLAGS += -std=c++20 -fcoroutines
QMAKE_CXXFLAGS += -std=c++20 -fcoroutines
}
二、协程简单封装 CCoroTask.h 代码:
javascript
#pragma once
#include <coroutine>
#include <future>
#include <chrono>
#include <thread>
#include <type_traits>
#include <QCoreApplication>
// 通用协程 Task 模板
template<typename F>
class CFutureAwaiter
{
public:
using R = std::invoke_result_t<F>;
explicit CFutureAwaiter(F && func) {
fut_ = std::async(std::launch::async, std::forward<F>(func));
}
bool await_ready() const noexcept {
#if 0
return false;
#else
return fut_.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
#endif
}
void await_suspend(std::coroutine_handle<> h) {
std::thread([h, this]() mutable {
fut_.wait(); // 确保执行结束
// 核心:让线程回到UI线程
QMetaObject::invokeMethod(qApp, [h]() { h.resume(); }, Qt::QueuedConnection);
}).detach();
}
#if 0
void await_resume() requires std::is_void_v<T> {}
T await_resume() requires (!std::is_void_v<T>) {
return fut_.get();
}
#else
auto await_resume() {
if constexpr (std::is_void_v<R>) {
fut_.get();
return;
} else {
return fut_.get();
}
}
#endif
private:
std::future<R> fut_;
};
// 通用协程 Task 模板
template<typename T> struct CoroTask
{
struct promise_type
{
T result_;
//返回协程的句柄
CoroTask<T> get_return_object() {
//return {};
return CoroTask<T>{
std::coroutine_handle<promise_type>::from_promise(*this)
};
}
// std::suspend_always:表示协程在每次挂起时都会等待,直到显式调用 resume() 恢复协程。
// std::suspend_never:表示协程在挂起时不会等待,直接返回控制权给调用者。
//协程开始时是否挂起
std::suspend_never initial_suspend() noexcept { return {}; }
//协程结束时是否挂起
std::suspend_never final_suspend() noexcept { return {}; }
//协程返回时是否返回值
template<typename U = T> requires (!std::is_void_v<U>)
void return_value(U&& val) {
//result_ = std::forward<U>(val);
result_ = std::move(val);
}
//void return_void() requires std::is_void_v<T> {}
//处理未捕获的异常
void unhandled_exception() { std::terminate(); }
};
using HandleType = std::coroutine_handle<promise_type>;
HandleType handle_;
CoroTask(HandleType h) : handle_(h) {}
CoroTask(const CoroTask&) = delete;
CoroTask(CoroTask && s) : handle_(s.handle_)
{
s.handle_ = nullptr;
}
~CoroTask() {
#if 0
//析构后 可能协程还要进行恢复,引起崩溃
if(this->handle_)
this->handle_.destroy();
#endif
}
CoroTask& operator=(const CoroTask&) = delete;
CoroTask& operator=(CoroTask&& s)
{
if(this->handle_) this->handle_.destroy();
this->handle_ = s.handle_;
s.handle_ = nullptr;
return *this;
}
// 提供外部获取返回值的接口
T get_result() const {
return handle_.promise().result_;
}
};
template<> struct CoroTask<void>
{
struct promise_type
{
//返回协程的句柄
CoroTask get_return_object() { return {}; }
//协程开始时是否挂起
std::suspend_never initial_suspend() noexcept { return {}; }
//协程结束时是否挂起
std::suspend_never final_suspend() noexcept { return {}; }
//协程返回时是否返回值
void return_void() {}
//处理未捕获的异常
void unhandled_exception() { std::terminate(); }
};
};
三、调用协程 相关代码:
javascript
#include "CCoroTask.h"
#include <chrono>
using namespace std::chrono_literals;
CoroTask<int> example1(QPushButton *pBtn, QLabel *pLabel)
{
pBtn->setEnabled(false);
// 执行耗时操作
auto result = co_await CFutureAwaiter([]()->int {
std::this_thread::sleep_for(3s);
return 100;
});
// 回到主线程,安全更新 UI
pLabel->setText("test 1: " + QString::number(result));
pBtn->setEnabled(true);
co_return result;
}
CoroTask<void> example2(QPushButton *pBtn, QLabel *pLabel) {
pBtn->setEnabled(false);
// 异步执行耗时操作
co_await CFutureAwaiter([]()->void {
std::this_thread::sleep_for(3s);
});
// 回到主线程,安全更新 UI
pLabel->setText("test 2: ok");
pBtn->setEnabled(true);
co_return;
}
connect(ui->test1Btn, &QPushButton::clicked, this, [this]() {
auto ret = example1(ui->test1Btn, ui->label);
//调用example1后,内部co_await,会立马返回到主线程执行
//所以ret在此时还没有返回,使用的话也不是最后返回的值
//不推荐使用带返回值的
});
connect(ui->test2Btn, &QPushButton::clicked, this, [this](){
example2(ui->test2Btn, ui->label);
});