利用Qt信号槽和std::function将任务放在主线程(GUI线程)运行
- 声明一个添加任务接口,信号和槽函数各两个(返回类型void和非void)。添加任务接口的参数为std::function类型。信号与槽函数的第一个参数是std::function类型的任务,第二个参数则为任务的返回值。
class TestQtMainThread : public QWidget {
Q_OBJECT
public:
TestQtMainThread(QWidget* parent = Q_NULLPTR);
template <typename T>
T AppendTask(const std::function<T()>& task);
signals:
void SIGAppendTask(const std::function<QVariant()>& task, QVariant& ret);
void SIGAppendTask(const std::function<void()>& task);
private slots:
void ONAppendTask(const std::function<QVariant()>& task, QVariant& ret);
void ONAppendTask(const std::function<void()>& task);
private:
Ui::TestQtMainThreadClass ui;
};
- 使用Qt::BlockingQueuedConnection方式连接信号与槽函数
connect(this,
qOverload<const std::function<QVariant()> &, QVariant &>(
&TestQtMainThread::SIGAppendTask),
this,
qOverload<const std::function<QVariant()> &, QVariant &>(
&TestQtMainThread::ONAppendTask),
Qt::BlockingQueuedConnection);
connect(
this,
qOverload<const std::function<void()> &>(
&TestQtMainThread::SIGAppendTask),
this,
qOverload<const std::function<void()> &>(&TestQtMainThread::ONAppendTask),
Qt::BlockingQueuedConnection);
- 槽函数定义
void TestQtMainThread::ONAppendTask(const std::function<QVariant()> &task,
QVariant &ret) {
ret = task();
}
void TestQtMainThread::ONAppendTask(const std::function<void()> &task) {
task();
}
- 接口定义
template <typename T>
inline T TestQtMainThread::AppendTask(const std::function<T()>& task) {
// 信号槽无法对没特化的tempalte进行connect,使用QVariant转换
// 转换包括:1.使用lambda包裹task转换其返回类型;2.转换参数类型
QVariant ret;
// 避免BlockingQueuedConnection死锁
// this->thread() == QCoreApplication::instance()->thread()
if (QThread::currentThread() == this->thread()) {
ONAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
} else {
emit SIGAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
}
return ret.value<T>();
}
// void特化版
template <>
inline void TestQtMainThread::AppendTask(const std::function<void()>& task) {
if (QThread::currentThread() == this->thread()) {
ONAppendTask(task);
} else {
emit SIGAppendTask(task);
}
}
- 使用
// WorkerThread是QThread子类,此段函数在非主线程运行
void WorkerThread::run() {
auto result = main_thread_->AppendTask<QString>([]() {
if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
return "error!";
}
return "ok!";
});
qDebug() << result;
// QWidget::move(...),此函数不允许在非主线程调用
main_thread_->AppendTask<void>([&]() { main_thread_->move({0, 0}); });
}
覆盖QWidget::move(...),非子线程可直接调用TestQtMainThread::move(...)
void TestQtMainThread::move(const QPoint &pos) {
this->AppendTask<void>([&] { this->QWidget::move(pos); });
}

浙公网安备 33010602011771号