qt 数据库操作(Qt多线程4种实现方式:从入门到避坑,一篇讲透)

qt 数据库操作(Qt多线程4种实现方式:从入门到避坑,一篇讲透)
Qt多线程4种实现方式:从入门到避坑,一篇讲透

一、引言

在Qt开发中,界面卡顿几乎是所有新手都会踩的坑——当你在主线程中处理文件读写、网络请求、大数据计算等耗时操作时,UI线程被阻塞,窗口无响应、按钮点击没反馈,用户体验大打折扣。而多线程,正是解决这一问题的核心方案。

Qt为开发者提供了4种风格迥异的多线程实现方式:从最基础的QThread继承重写,到轻量级的

moveToThread,再到高封装的Qt Concurrent,以及面向任务的QRunnable。它们各有适用场景、优缺点分明,选对方式能让代码效率翻倍,选错则可能引发线程安全、内存泄漏等棘手问题。

本文将逐一拆解这4种实现方式的原理、代码实战、适用场景和避坑要点,帮你彻底掌握Qt多线程的核心用法。

二、Qt 4种多线程实现方式

qt 数据库操作(Qt多线程4种实现方式:从入门到避坑,一篇讲透)

方式1:继承QThread类并重写run()函数

应用场景

  1. 简单的、无事件循环的纯耗时任务:如一次性的大数据计算、文件解析,无需和主线程频繁通信;
  2. 需完全掌控线程生命周期的场景:
1 可直接调用setPriority()/setStackSize()等接口,精准定制线程优先级、栈大小等系统级属性,这是 moveToThread 等解耦方式难以高效实现的;2 线程生命周期与 run () 执行周期强绑定,启动即执行、执行完即退出,无需额外管理事件循环,适配一次性任务;3 无信号槽触发延迟,执行时机精准,满足实时性要求高的场景。

3. 兼容旧代码:一些老项目仍使用该方式,维护时需了解其坑点。

坑点说明

#include #include #include #include #include #include #include #include // 共享数据(模拟多线程访问)struct GlobalData {    static int sharedValue; // 共享变量    static QMutex mutex;    // 保护共享变量的互斥锁};int GlobalData::sharedValue = 0;QMutex GlobalData::mutex;// 自定义线程类(继承QThread,包含新手易踩的坑)class BadThread : public QThread{    Q_OBJECTpublic:    BadThread(QWidget* parent = nullptr) : QThread(parent), m_uiLabel(nullptr) {        // 坑点1:在构造函数中创建UI控件(QThread对象属于主线程)        m_uiLabel = new QLabel("线程状态:未启动", parent);        // 坑点2:直接连接信号槽(线程上下文错误)        connect(this, &BadThread::valueUpdated, this, &BadThread::updateLabel, Qt::DirectConnection);    }    ~BadThread() {        // 坑点3:未等待线程结束就释放资源        qDebug() << "线程析构";    }signals:    void valueUpdated(int value); // 通知更新值private slots:    // 试图更新UI标签(典型坑点)    void updateLabel(int value) {        qDebug() << "updateLabel线程ID:" << QThread::currentThreadId();        m_uiLabel->setText(QString("当前值:%1").arg(value));    }protected:    void run() override {        qDebug() << "run()线程ID:" << QThread::currentThreadId();        // 模拟耗时任务:循环修改共享变量        for (int i = 0; i < 10; i++) {            // 坑点4:误以为QThread的成员变量属于子线程            msleep(500); // 这里能调用msleep是因为run()在子线程,但成员变量仍属主线程            // 保护共享变量(正确做法,对比坑点)            QMutexLocker locker(&GlobalData::mutex);            GlobalData::sharedValue++;            locker.unlock();            // 发送信号更新UI(错误的连接方式)            emit valueUpdated(GlobalData::sharedValue);        }        // 坑点5:直接调用quit()/exit()(无效,run()结束线程自动退出)        this->quit();        qDebug() << "run()执行完毕";    }private:    QLabel* m_uiLabel; // QThread对象的成员变量,属于主线程!};// 主线程调用int main(int argc, char *argv[]){    QApplication a(argc, argv);    qDebug() << "主线程ID:" << QThread::currentThreadId();    QWidget window;    window.resize(300, 100);    QVBoxLayout* layout = new QVBoxLayout(&window);    BadThread thread(&window);    layout->addWidget(thread.m_uiLabel); // 将标签加入UI    // 启动线程    thread.start();    // 坑点6:未判断线程状态就调用wait()(可能导致主线程阻塞)    // thread.wait(); // 若打开,UI会卡死    window.show();    return a.exec();}

坑点 1:混淆QThread 对象和子线程的归属(最核心的坑)

错误认知:以为继承 QThread 后,整个子类对象(包括成员变量、槽函数)都属于子线程;

真实情况:QThread 本身是线程控制器,QThread 对象(包括其成员变量、构造 / 析构函数、除 run () 外的所有函数)都属于创建它的线程(通常是主线程),只有run()方法在子线程中执行;

问题:若在 run () 中直接操作m_uiLabel(属于主线程),会导致跨线程操作 UI 控件,导致崩溃;

避坑方案

  1. 绝对不要在 QThread 子类中定义 UI 相关成员变量;
  2. 若需更新 UI,必须通过信号槽(默认的Qt::AutoConnection,Qt 会自动切换到主线程执行);
  3. 牢记:只有 run () 函数内的代码属于子线程,其他部分均属主线程。

坑点 2:信号槽连接方式错误导致线程安全问题

  • 错误做法:Qt::QDirectConnection表示信号发送线程直接调用槽函数,即updateLabel会在子线程中执行,而m_uiLabel属于主线程,导致跨线程操作 UI;
  • 避坑方案: 使用默认的Qt::AutoConnection(Qt 自动判断:若信号和槽不在同一线程,会通过事件循环异步调用,保证线程安全); 若需手动指定,用Qt::QueuedConnection(队列连接),槽函数会在接收者的线程(主线程)执行。

坑点 3:线程退出和资源释放的坑

错误场景 1:直接调用QThread::terminate()强制终止线程

• 问题本质:terminate()是操作系统级别的 “暴力终止”,会直接杀死子线程,不会给线程任何清理资源的机会;

• 严重后果:若子线程正操作共享数据(如写入文件、修改内存数据、访问数据库),强制终止会导致数据损坏、内存泄漏,甚至因未释放互斥锁引发死锁;此外,该接口的行为依赖操作系统,跨平台时可能出现不可预期的问题。

错误场景 2:析构函数中未等待线程结束就释放资源

• 问题本质:QThread 对象的析构函数在主线程执行,若子线程仍在运行(run () 未执行完),析构函数会提前释放线程的成员变量(如 UI 控件、数据缓存);

• 严重后果:run () 中若继续访问已释放的成员变量,会触发野指针访问,直接导致程序崩溃。

避坑方案 :核心原则:绝对禁止使用terminate(),改用 “优雅退出标记” 让线程主动退出;析构函数中先等待线程完全结束,再释放资源;

#include #include #include class SafeThread : public QThread{    Q_OBJECTpublic:    explicit SafeThread(QObject *parent = nullptr) : QThread(parent) {}    // 析构函数:安全退出线程+释放资源    ~SafeThread() override {        // 步骤1:发送中断请求(设置优雅退出标记)        requestInterruption();        // 步骤2:通知事件循环退出(若线程内启动了exec())        quit();        // 步骤3:等待线程结束(超时3秒,避免主线程永久阻塞)        if (!wait(3000)) {            qWarning() << "线程未正常退出,强制终止(仅作为最后兜底)";            terminate(); // 兜底方案,仅超时后使用            wait(1000);  // 等待强制终止完成        }        qDebug() << "线程资源已安全释放";    }protected:    void run() override {        // 循环中高频检测中断标记,保证及时退出        while (!isInterruptionRequested()) {            // 模拟业务逻辑:操作共享数据/占用资源            qDebug() << "子线程执行中,操作共享资源...";            msleep(500); // 模拟耗时操作        }        // 优雅退出前的清理逻辑(释放资源、保存数据)        qDebug() << "子线程优雅退出,清理资源完成";    }};// 主线程调用示例int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    SafeThread thread;    thread.start(); // 启动线程    // 模拟主线程运行一段时间后退出    QTimer::singleShot(2000, &a, &QCoreApplication::quit);    return a.exec();}

坑点 4:GUI 程序中滥用 wait () 导致主线程阻塞、UI 卡死

C++ 非 GUI 程序(如后台服务、控制台程序)中,std::thread::join()是安全且常用的线程同步手段 —— 其核心作用是阻塞主线程,等待子线程执行完毕后再退出,避免主线程提前终止导致子线程任务中断、资源泄漏。

但 Qt GUI 程序的主线程(UI 线程)承载了UI 事件循环(QApplication::exec ()),其核心职责是持续处理鼠标点击、窗口重绘、键盘输入等 UI 事件。若在 GUI 程序中调用QThread::wait()(或等价的join()),主线程会进入阻塞状态,无法轮询 UI 事件循环:

  • 直观表现:窗口卡死、按钮点击无响应、界面无法重绘;
  • 本质原因:UI 线程的核心职责(处理人机交互)被阻塞,而非单纯的 “线程同步” 问题。

避坑方案:

  • GUI 程序:用信号槽异步通知替代 wait ()/join () —— 子线程结束后通过finished()信号通知主线程执行后续逻辑,主线程全程保持事件循环运行,不阻塞 UI;
  • 非 GUI 程序:可放心使用join()/wait()实现线程同步,核心解决 “主线程提前退出导致子线程终止” 的问题。
#include #include #include class SafeThread : public QThread{    Q_OBJECTpublic:    explicit SafeThread(QObject *parent = nullptr) : QThread(parent) {}    // 析构函数:安全退出线程+释放资源    ~SafeThread() override {        // 步骤1:发送中断请求(设置优雅退出标记)        requestInterruption();        // 步骤2:通知事件循环退出(若线程内启动了exec())        quit();        // 步骤3:等待线程结束(超时3秒,避免主线程永久阻塞)        if (!wait(3000)) {            qWarning() << "线程未正常退出,强制终止(仅作为最后兜底)";            terminate(); // 兜底方案,仅超时后使用            wait(1000);  // 等待强制终止完成        }        qDebug() << "线程资源已安全释放";    }protected:    void run() override {        // 循环中高频检测中断标记,保证及时退出        while (!isInterruptionRequested()) {            // 模拟业务逻辑:操作共享数据/占用资源            qDebug() << "子线程执行中,操作共享资源...";            msleep(500); // 模拟耗时操作        }        // 优雅退出前的清理逻辑(释放资源、保存数据)        qDebug() << "子线程优雅退出,清理资源完成";    }};// 主线程调用示例int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    SafeThread thread;    thread.start(); // 启动线程    // 模拟主线程运行一段时间后退出    QTimer::singleShot(2000, &a, &QCoreApplication::quit);    return a.exec();}

解惑点:为什么优雅退出线程时调用了 wait (),这和 GUI 程序禁用 wait () 不是矛盾?

两种 wait () 的调用场景完全不同!

优雅退出的 wait ():调用wait()前,已经通过requestInterruption()/m_isStop让子线程 “主动退出循环”—— 子线程的 run () 函数会快速执行完剩余逻辑并结束,wait()只是等待 “已准备好退出的线程” 完成收尾,阻塞时间极短(毫秒级),用户完全感知不到 UI 卡顿。

错误场景的 wait ():子线程还在执行耗时任务(比如无限循环处理数据),调用thread.wait()会让主线程无限阻塞(直到子线程自然结束),此时 UI 事件循环长期停摆,必然卡死;

//析构函数中先停止线程再释放资源~BadThread() {    requestInterruption();    quit();    wait(); // 等待线程结束    delete m_uiLabel; // 安全释放资源}

进阶优化:给 wait () 加超时(进一步规避风险)

为了绝对避免极端情况(比如标记位失效导致子线程仍阻塞),工程化中会给 wait () 加超时,确保主线程不会永久阻塞:

~BadThread() {    requestInterruption();    quit();    // 最多等待3秒,超时则兜底(仅作为最后手段)    if (!wait(3000)) {         qWarning() << "线程未正常退出,强制终止(兜底)";        terminate(); // 仅兜底,优先保证程序不卡死        wait(1000); // 等待强制终止完成    }    delete m_uiLabel;}

坑点 5:误以为 run () 中调用 quit ()/exit () 能控制线程

  • 错误认知:在 run () 末尾调用this->quit(),以为能退出线程;
  • 真实情况
  • quit()/exit()的作用是退出线程的事件循环,而默认的run()方法只是执行完代码就结束,没有启动事件循环(除非在 run () 中调用exec());
  • 因此,run () 中调用quit()完全无效,线程会在 run () 执行完毕后自动退出;
  • 避坑方案:
  • 1. 若不需要子线程的事件循环(如纯耗时计算),无需调用quit()/exec();
  • 2. 若需子线程处理信号槽(如持续监听事件),才在 run () 中调用exec()启动事件循环;quit()才有效;
void run() override {    // 启动子线程的事件循环    QEventLoop loop;    connect(this, &BadThread::stopSignal, &loop, &QEventLoop::quit);    loop.exec(); // 事件循环阻塞,直到收到stopSignal}

解惑点:在QThread 的 run () 中调用 exec () 启动事件循环的核心作用是什么?

  1. run () 中调用 exec () 的核心作用:启动子线程的事件循环,让子线程从 “一次性任务执行器” 变为 “常驻型事件处理器”,能持续响应信号槽、定时器、网络事件等;
  2. 适用场景:需要子线程长期运行、持续监听外部事件(如后台服务、实时数据采集、网络通信);

关键注意事项(避坑)

  1. exec () 必须在 run () 中调用:QThread 的事件循环是线程专属的,只有在 run ()(子线程上下文)中调用 exec (),才能让事件循环运行在子线程中;
  2. moveToThread 方式实现多线程时: 默认 run () 会调用 exec (),所以无需手动调用exec();
  3. exec () 阻塞但不占用 CPU:事件循环阻塞时,线程会进入 “等待事件” 状态(调用操作系统的 epoll/WaitForSingleObject),不会空轮询占用 CPU;
  4. 退出顺序:需先停止定时器 / 网络组件,再调用 quit (),避免事件循环退出后还有未处理的事件导致崩溃。

坑点 6:共享数据未加锁导致数据竞争

错误场景:

若多个继承 QThread 的子类同时操作GlobalData::sharedValue,未加锁会导致变量值错乱(如两个线程同时 ++,结果只加 1);

• 避坑方案:

1. 用QMutex + QMutexLocker保护共享数据,遵循 “最小锁持有时间” 原则;

2. 简单变量(如 bool、int)优先用std::atomic(原子变量),无需加锁,效率更高。

总结:

  1. 此方案核心坑点是混淆 QThread 对象和子线程的归属,牢记只有 run () 在子线程,其余均属主线程;
  2. 避坑关键是:不操作 UI、正确使用信号槽连接方式、优雅退出线程、保护共享数据;
  3. 工程化开发中,除非有特殊需求,否则优先选择 moveToThread 方案,避免继承 QThread 的诸多陷阱。

方式2:moveToThread(推荐型,解耦设计)

moveToThread(QThread *targetThread)的作用是修改 QObject 的线程归属,但有一个前提:

Qt 官方规则:只有无父对象的 QObject,才能调用 moveToThread 修改线程归属;若 QObject 有父对象,moveToThread 会直接静默失败(无报错、无警告)。

Qt 父对象机制的核心规则:

• 生命周期管理:父对象销毁时,会自动销毁所有子对象(避免内存泄漏);

• 线程归属绑定:子对象的线程归属会强制继承父对象的线程归属,且无法单独修改(除非解除父子关系)。

解决冲突的核心方案:

思路:解耦生命周期管理和线程归属

1.让 Worker 保持无父对象

2.用信号槽+deleteLater替代父对象的生命周期管理,既避免内存泄漏,又不违反线程归属规则

关键注意点

  • moveToThread后,worker对象的所有槽函数都会在子线程中执行;
  • 不能在worker对象的构造函数中编写耗时逻辑(构造时还未移动到子线程);
  • 线程退出时需正确释放资源(用信号槽+deleteLater方式,避免内存泄漏)。

示例代码

C++#include #include #include #include // 业务逻辑类(纯逻辑,不继承QThread)class Worker : public QObject{Q_OBJECTpublic slots:// 耗时操作函数(通过信号触发)void doWork() {qDebug() << "工作线程ID:" << QThread::currentThreadId();for (int i = 0; i < 10; i++) {  QThread::msleep(500);qDebug() << "处理任务:" << i;}// 任务完成,发送信号通知主线程emit workFinished();}signals:void workFinished(); // 任务完成信号};int main(int argc, char *argv[]){QCoreApplication a(argc, argv);qDebug() << "主线程ID:" << QThread::currentThreadId();// 1. 创建空线程QThread* thread = new QThread();// 2. 创建业务对象Worker* worker = new Worker();// 3. 将业务对象移动到子线程worker->moveToThread(thread);// 4. 连接信号槽:启动线程后触发耗时任务QObject::connect(thread, &QThread::started, worker, &Worker::doWork);// 5. 任务完成后,退出线程并释放资源QObject::connect(worker, &Worker::workFinished, thread, &QThread::quit);QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater);// 6. 启动线程thread->start();return a.exec();}

适用场景:绝大多数业务场景(如网络请求、文件读写、数据库操作),是Qt多线程的首选方案;

坑点 1:给 Worker 设置父对象后,调用 moveToThread(未分离父子关系)

// 错误:给Worker设置父对象后调用moveToThreadQWidget* parentWidget = new QWidget();Worker* worker = new Worker(parentWidget); // 绑定父对象QThread* thread = new QThread();worker->moveToThread(thread); // 移动失败!

严重后果

• Qt 规定:有父对象的 QObject 无法被 moveToThread,上述代码中moveToThread会静默失败(无报错,但 Worker 仍在主线程执行),最终还是单线程,耗时任务导致 UI 卡死;

坑点 2:moveToThread 成功后,仍手动操作 Worker 的父对象(移动后操作父对象)

// 错误示例:moveToThread成功后,强行设置父对象Worker* worker = new Worker(); // 无父对象,正确QThread* thread = new QThread();worker->moveToThread(thread); // 移动成功,Worker归属子线程thread->start();QWidget* mainWidget = new QWidget();worker->setParent(mainWidget); // 错误:给子线程的Worker绑定主线程的父对象


坑点 3:跨线程直接调用 Worker 的成员函数

class Worker : public QObject {    Q_OBJECTpublic slots:    void doWork() { /* 耗时任务 */ }    // 普通成员函数    void setParam(int param) { m_param = param; }private:    int m_param;};// 主线程调用Worker* worker = new Worker();QThread* thread = new QThread();worker->moveToThread(thread);thread->start();worker->setParam(10); // 错误:主线程直接调用Worker的普通函数,即使是槽函数也是一样会出问题

坑点 4:信号槽连接方式错误导致线程安全问题

// Worker的doWork是耗时任务,绑定到子线程Worker* worker = new Worker();QThread* thread = new QThread();worker->moveToThread(thread);// 错误:强制使用DirectConnectionconnect(this, &MainWindow::startWork, worker, &Worker::doWork, Qt::DirectConnection);thread->start();emit startWork(); // doWork会在主线程执行,UI卡死

坑点 5:线程 / Worker 资源释放不规范(内存泄漏 / 崩溃)

// 错误:启动线程后未绑定finished信号,线程/Worker永久内存泄漏Worker* worker = new Worker();QThread* thread = new QThread();worker->moveToThread(thread);connect(this, &MainWindow::startWork, worker, &Worker::doWork);thread->start();// 未调用deleteLater,线程/Worker一直占用内存

坑点 6:Worker 中创建子对象未指定父对象,导致归属错误

class Worker : public QObject {    Q_OBJECTpublic slots:    void doWork() {        // 错误:创建子对象时未指定父对象为this        QTimer* timer = new QTimer();         timer->start(1000);    }};

说明:

  1. Worker 移动到子线程后,其内部创建的子对象(如QTimer)若未指定父对象为this,

该子对象的线程归属仍为主线程;

  1. 子线程中操作主线程的QTimer,会触发跨线程访问警告,甚至定时器不工作。

方式3:Qt Concurrent(高级型,无感知线程)

Qt Concurrent是Qt提供的高层封装多线程接口(基于Qt Concurrent模块),核心是通过函数式编程实现多线程,无需手动管理线程,Qt会自动创建线程池并分配任务。

核心原理

  • 基于线程池(QRunnable + QThreadPool),自动管理线程的创建、复用、销毁;
  • 提供QtConcurrent::run()、map()、filter()等接口,直接将函数放入线程池执行;
  • 无需继承任何类,一行代码即可实现多线程。

坑点 1:传入临时对象 / 悬空指针,导致任务中访问已释放资源

#include #include class DataHandler {public:    void processData() {        qDebug() << "处理数据:" << m_data;    }    QString m_data = "测试数据";};void test() {    // 错误:temp是栈上临时对象,函数结束后立即销毁    DataHandler temp;    // 将临时对象的成员函数传入QtConcurrent    QtConcurrent::run(&temp, &DataHandler::processData);} // temp销毁,子线程中processData访问m_data时触发野指针崩溃int main() {    test();    // 主线程休眠,让子线程有机会执行    QThread::sleep(1);    return 0;}

坑点 2:未等待任务完成就退出主线程,导致任务被强制终止

void longTask() {    // 耗时10秒的任务:写入文件    QFile file("test.txt");    file.open(QIODevice::WriteOnly);    for (int i = 0; i < 10000; i++) {        file.write("数据行\n");        QThread::msleep(1);    }    file.close(); // 若任务被强制终止,文件未关闭,数据损坏}int main() {    // 提交耗时任务    QtConcurrent::run(longTask);    // 错误:主线程直接退出,未等待任务完成    return 0; }

后果 :

  1. 主线程退出时,Qt 会强制终止线程池中的所有任务,导致任务内的资源(如文件句柄、网络连接)未正常关闭,引发数据损坏、资源泄漏;
  2. 若任务操作共享数据,强制终止还会导致数据错乱。

坑点 3:共享资源未加锁,多任务并发访问导致数据竞争

坑点 4:任务内创建的资源未释放,导致内存泄漏

总结:核心避坑原则

1. 传入任务的对象必须保证 “生命周期覆盖任务执行周期”(优先用智能指针);

2. 任务内的动态资源必须显式释放(或用智能指针自动管理);

3. 主线程需等待任务完成后再退出(用QFuture/QFutureWatcher监控任务状态);



3.1(同步等待):future.waitForFinished()—— 阻塞主线程,适合非 GUI 程序;3.2(异步等待):QFutureWatcher—— 通过finished()信号通知主线程,适合 GUI 程序;

4. 共享资源必须加锁(QMutex/QReadWriteLock)保护。

示例:场景 1:GUI 程序中使用 Qt Concurrent(异步等待 + 资源释放)

// GUI程序中避免阻塞UI,用QFutureWatcher异步等待void MainWindow::on_startTask_clicked() {    QSharedPointer handler(new SafeDataHandler());    QFuture future = QtConcurrent::run(handler.data(), &SafeDataHandler::processData);    QFutureWatcher* watcher = new QFutureWatcher();    connect(watcher, &QFutureWatcher::finished, this, [=]() {        // 任务完成后处理结果+释放资源        qDebug() << "任务完成,总和:" << handler->getTotal();        watcher->deleteLater(); // 释放watcher    });    watcher->setFuture(future);}


场景 2:取消正在执行的任务(优雅退出)

// 取消任务,避免强制终止导致资源泄漏QFuture future = QtConcurrent::run(longTask);// 5秒后取消任务QTimer::singleShot(5000, [&]() {    future.cancel(); // 标记任务取消    future.waitForFinished(); // 等待任务响应取消并清理资源});//注意:任务函数内需要检测取消状态(QFuture::isCanceled()),否则cancel()无效:void longTask() {    QFile file("test.txt");    file.open(QIODevice::WriteOnly);    for (int i = 0; i < 10000; i++) {        // 检测取消状态,优雅退出        if (QThread::currentThread()->isInterruptionRequested()) {            file.close(); // 清理资源            return;        }        file.write("数据行\n");        QThread::msleep(1);    }    file.close();}

适用场景:短期、轻量、批量的任务(如图片批量处理、数据批量解析),适合快速实现多线程,无需精细控制线程。

方式4:QRunnable + QThreadPool(轻量级任务池)

将单次 / 短期任务封装到QRunnable子类中,提交给全局 / 自定义线程池执行,线程池自动管理线程的创建、复用、销毁,无需手动控制线程生命周期。核心优势是 “低开销、高复用”,适合大量短小的异步任务(如批量数据解析、多文件读取)。

核心避坑要点:

  1. 线程安全:共享资源必须加锁,取消标记用原子变量,避免数据竞争;
  2. 任务可控:自定义取消标记实现中途终止,避免任务 “提交后无法停止”;
  3. 线程池管理:区分全局 / 自定义线程池,合理设置线程数和超时时间,避免核心任务阻塞;
  4. 资源释放:根据autoDelete状态管理任务对象,析构时清空任务队列并等待完成;
  5. 跨线程通信:通过invokeMethod/ 信号槽异步通知主线程,禁止子线程直接操作 UI。

文章版权声明:除非注明,否则均为边学边练网络文章,版权归原作者所有