3. 启动与停止线程:start()、quit()、wait()、terminate()的正确用法

线程的启动和停止,看起来简单,其实坑不少。

我见过不少新手,直接调用terminate()来杀线程,结果程序莫名其妙崩溃。也见过有人忘了调用wait(),程序退出时直接段错误。

说白了,这四个函数各有各的脾气。用对了,线程管理得心应手;用错了,调试到怀疑人生。

3.1 start():让线程跑起来

start()是线程的起点。调用它之后,Qt会创建一个新的执行线程,然后执行run()函数里的代码。

核心要点:

  • 一个线程对象只能调用一次start()。重复调用会触发警告,且不会生效。
  • 调用start()后,线程不一定立即执行。它只是进入了就绪队列,等待操作系统调度。
  • 如果你继承QThread,记得重写run()方法。否则线程会直接退出。
// 正确的启动方式
class WorkerThread : public QThread
{
    Q_OBJECT
protected:
    void run() override
    {
        // 在这里执行耗时操作
        for (int i = 0; i < 100; ++i) {
            qDebug() << "Working..." << i;
            QThread::msleep(100);
        }
    }
};

// 使用
WorkerThread *thread = new WorkerThread(this);
thread->start();  // 启动线程

我的习惯:启动线程后,我会立即检查isRunning(),确认线程真的跑起来了。虽然很少失败,但多一步检查总没坏处。

3.2 quit():优雅地通知线程退出

quit()是Qt推荐的线程退出方式。它不会强制终止线程,而是告诉线程的事件循环:「你可以结束了」。

注意,quit()只对运行了事件循环的线程有效。如果你的线程没有调用exec(),那quit()就没什么用。

// 带事件循环的线程
class EventLoopThread : public QThread
{
    Q_OBJECT
public:
    void stop()
    {
        quit();  // 通知事件循环退出
        wait();  // 等待线程真正结束
    }

protected:
    void run() override
    {
        exec();  // 启动事件循环
    }
};

我曾经踩过的坑:在run()里写了一个while(true)循环,然后调用quit()。结果线程根本停不下来。为什么?因为quit()只退出事件循环,不会中断你的自定义循环。你需要自己检查一个标志位来退出。

3.3 wait():等待线程真正结束

wait()的作用是阻塞当前线程,直到目标线程执行完毕。它接受一个超时参数(毫秒),默认是ULONG_MAX,表示一直等下去。

我个人建议,每次停止线程后都要调用wait()。否则,线程可能还在后台运行,而你的程序已经销毁了线程对象,导致崩溃。

场景 建议做法
程序退出前 先quit(),再wait()
切换工作线程 先quit(),再wait(),再start()
强制终止 先terminate(),再wait()(不推荐)
// 安全的线程停止流程
void stopThread(QThread *thread)
{
    if (thread->isRunning()) {
        thread->quit();          // 第一步:通知退出
        thread->wait(3000);      // 第二步:等待最多3秒
        if (thread->isRunning()) {
            // 超时了还没结束,说明有问题
            qWarning() << "Thread did not stop in time!";
            thread->terminate(); // 最后手段
            thread->wait();      // 别忘了再等一次
        }
    }
}

避坑指南:不要在GUI线程里调用wait()且不设超时。我曾经这么干过,结果界面卡死了。因为wait()会阻塞主线程的事件循环,导致界面无法响应。如果你必须等,设个超时,比如1000毫秒。

3.4 terminate():最后的杀手锏

terminate()会强制终止线程,不管线程正在做什么。它不释放资源,不调用析构函数,不通知任何对象。

说白了,这就是个核武器。能不用就别用。

使用terminate()的风险:

  • 线程可能正在持有互斥锁,强制终止会导致死锁。
  • 线程可能正在分配内存,强制终止会导致内存泄漏。
  • 线程可能正在写入文件,强制终止会导致数据损坏。
// 只有在万不得已时才使用
if (thread->isRunning()) {
    thread->terminate();  // 强制终止
    thread->wait();       // 必须等待,否则线程对象可能还在运行
}

我的建议:永远不要在生产代码里直接调用terminate()。如果你发现线程无法正常退出,那说明你的线程设计有问题。应该去修复线程的退出逻辑,而不是用暴力手段。

3.5 完整的线程生命周期管理

下面这张图展示了线程从创建到销毁的完整流程:

线程生命周期流程图 创建线程对象 start() 启动线程 线程运行中 quit() → 事件循环退出 terminate() → 强制终止 wait() 等待结束

从图中可以看出,quit()和terminate()是两条不同的路径。前者是绿色通道,安全可靠;后者是红色警报,非不得已不要走。

3.6 实战中的最佳实践

说了这么多理论,来点实际的。我在项目中总结了一套线程管理的模板,分享给你:

class SafeThread : public QThread
{
    Q_OBJECT
public:
    explicit SafeThread(QObject *parent = nullptr)
        : QThread(parent), m_stopRequested(false) {}

    void requestStop()
    {
        m_stopRequested = true;  // 设置标志位
        quit();                  // 通知事件循环退出
    }

    bool isStopRequested() const
    {
        return m_stopRequested;
    }

protected:
    void run() override
    {
        // 自定义循环中检查标志位
        while (!m_stopRequested) {
            // 执行你的工作
            QThread::msleep(100);
        }
    }

private:
    QAtomicInt m_stopRequested;  // 原子操作,线程安全
};

// 使用示例
SafeThread *worker = new SafeThread(this);
worker->start();

// 停止时
worker->requestStop();
if (!worker->wait(3000)) {
    qWarning() << "Worker thread did not stop, forcing...";
    worker->terminate();
    worker->wait();
}

总结一下:

  • start():启动线程,只能调用一次。
  • quit():优雅退出,配合事件循环使用。
  • wait():等待线程结束,必须调用。
  • terminate():强制终止,最后手段。

记住这个顺序:先quit,再wait,最后才考虑terminate。

嗯,线程的启动和停止就讲到这里。这些函数看着简单,但用好了能避免很多坑。下次遇到线程卡死的问题,先检查一下你的停止流程对不对。

可可精品资料库