Qt-QThread学习总结

QThread 类用于线程管理和操作。 下面是对每个函数的简要说明:

static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION;

  • 返回当前线程的唯一标识符。这个标识符类型为 Qt::HANDLE,通常是一个指向线程内部数据结构的指针或句柄。在跨平台情况下,它可能是一个底层操作系统的线程标识符。
static QThread *currentThread();
  • 返回指向当前线程对象的指针。这允许获取对当前线程的引用,以便对其进行操作,例如设置线程优先级、休眠等。

static int idealThreadCount() noexcept;

  • 返回当前系统中理想的线程数量。这个函数通常用于确定应该创建多少个线程,以使应用程序在给定的硬件环境中获得最佳性能。它可能受到系统资源和硬件限制的影响。

static void yieldCurrentThread();

  • 通知操作系统放弃当前线程的时间片,以便其他线程可以运行。这个函数被用于线程间的协作,可以帮助避免线程独占 CPU 资源。然而,它的行为可能因操作系统和编译器的不同而有所不同。

void setPriority(Priority priority);

  • 设置线程的优先级。优先级是一个枚举类型,用于指定线程的调度优先级。较高的优先级意味着线程在竞争 CPU 资源时更有可能被调度执行。常见的优先级有 LowPriority、NormalPriority、HighPriority 等。

Priority priority() const;

  • 返回当前线程的优先级。

bool isFinished() const;

  • 判断线程是否已经完成执行。如果线程已经执行完毕并退出,则返回 true,否则返回 false。

bool isRunning() const;

  • 判断线程是否正在运行。如果线程当前正在执行中,则返回 true,否则返回 false。

void requestInterruption();

  • 请求中断线程的执行。这通常用于向线程发出中断信号,告知线程应该尽快停止执行。

bool isInterruptionRequested() const;

  • 判断是否有中断请求。如果有中断请求,则返回 true,否则返回 false。

void setStackSize(uint stackSize);

  • 设置线程的堆栈大小。堆栈大小是线程运行时所使用的内存空间大小。

uint stackSize() const;

  • 返回线程的堆栈大小。

void exit(int retcode = 0);

  • 退出线程,并返回给定的退出码。默认情况下,退出码为 0,表示正常退出。这个函数会导致线程立即停止执行并退出。

*QAbstractEventDispatcher eventDispatcher() const;

  • 返回与当前线程关联的事件分发器对象。事件分发器负责将事件传递给线程中的对象,并处理事件队列中的事件。
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
  • 设置线程的事件分发器对象。可以通过这个函数来自定义线程的事件处理机制,例如使用自定义的事件分发器来处理事件。
bool event(QEvent event) override;
  • 重载函数,用于处理线程的自定义事件。当线程中有事件需要处理时,会调用这个函数来处理事件。你可以在这个函数中根据不同类型的事件执行相应的处理逻辑。

int loopLevel() const;

  • 返回线程事件循环的层级。这个函数用于跟踪事件循环的嵌套层级,可以帮助你了解当前线程中事件循环的状态。

start(Priority = InheritPriority)、terminate()、quit():

  • 这些是成员函数,可能用于启动、终止或退出某个线程。start 函数可能有一个参数 Priority,默认值为 InheritPriority,用于指定线程的优先级。terminate 函数可能用于强制终止线程的执行。quit 函数可能用于退出线程的执行。

wait 函数:

  • 这是一个成员函数,可能用于在线程中等待一段时间,直到特定条件满足或超时。它可能有两个版本,一个接受 QDeadlineTimer 参数,另一个接受 unsigned long 参数。在Qt6中,这个函数可能被声明为内联函数。

sleep、msleep、usleep 函数:

  • 这些是静态成员函数,用于在当前线程中让线程休眠指定的时间,单位可能是毫秒或微秒。

run 函数、exec 函数:

  • run 函数可能是用于线程执行的虚拟函数,需要在派生类中实现。exec 函数可能是用于执行线程的入口函数。

setTerminationEnabled 函数:

  • 这是一个静态成员函数,可能用于设置是否允许线程的终止。

QThread 实现多线程的几种方式

  1. 继承 QThread 类
  • 创建一个自定义的线程类,继承自 QThread。
  • 重写 run() 函数,在其中编写需要在新线程中执行的代码逻辑。
  • 实例化自定义线程类的对象,调用 start() 方法启动线程。
    class MyThread : public QThread
    {
    public:
        void run() override
        {
            // 执行需要在新线程中进行的操作
        }
    };
    

2.将对象移动到线程中:

  • 创建一个继承自 QObject 的类,作为需要在新线程中执行的对象。
  • 创建一个继承自 QThread 的线程类,并在其 run() 函数中实例化对象,并将其移动到新线程中。
  • 在主线程中启动新线程,并在其中执行对象的方法。
    class Worker : public QObject
    {
        Q_OBJECT
    public slots:
        void doWork() {
            // 执行需要在新线程中进行的操作
        }
    };
    
    class MyThread : public QThread
    {
    public:
        void run() override
        {
            Worker worker;
            worker.moveToThread(this);
            connect(this, &MyThread::startWork, &worker, &Worker::doWork);
            emit startWork();
        }
    signals:
        void startWork();
    };
    
    

3.使用 QtConcurrent:

  • 使用 QtConcurrent::run() 函数在后台线程中执行函数。
  • 可以通过 QFuture 对象来监视任务的状态,并获取返回值。
    #include <QtConcurrent>
    
    void myFunction() {
        // 执行需要在新线程中进行的操作
    }
    
    int main() {
        QFuture<void> future = QtConcurrent::run(myFunction);
        future.waitForFinished(); // 等待任务完成
        return 0;
    }
    

4.自定义事件循环:

  • 在主线程中创建一个自定义的事件循环。

  • 在新线程中执行需要在该线程中完成的任务,并通过事件来与主线程进行通信。

    #include <QCoreApplication>
    #include <QThread>
    #include <QDebug>
    #include <QTimer>
    //!!!注意声明与实现分离
    // 自定义工作线程类
    class WorkerThread : public QThread
    {
        Q_OBJECT
    public:
        WorkerThread(QObject *parent = nullptr) : QThread(parent) {}
    
        void run() override {
            // 模拟一个耗时的任务
            for (int i = 0; i <= 100; ++i) {
                // 发送进度更新信号
                emit progressChanged(i);
                msleep(100); // 模拟任务执行时间
            }
        }
    
    signals:
        void progressChanged(int progress);
    };
    
    // 主线程类
    class MainThread : public QObject
    {
        Q_OBJECT
    public:
        MainThread(QObject *parent = nullptr) : QObject(parent) {
            // 创建工作线程
            workerThread = new WorkerThread(this);
    
            // 连接工作线程的进度更新信号到主线程的槽函数
            connect(workerThread, &WorkerThread::progressChanged, this, &MainThread::onProgressChanged);
    
            // 启动工作线程
            workerThread->start();
        }
    
        ~MainThread() {
            // 等待工作线程结束
            workerThread->wait();
        }
    
    public slots:
        // 接收并处理工作线程的进度更新信号
        void onProgressChanged(int progress) {
            qDebug() << "Progress:" << progress;
            // 在这里更新 UI
        }
    
    private:
        WorkerThread *workerThread;
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        // 创建主线程对象
        MainThread mainThread;
    
        return a.exec();
    }
    
    

注意事项 !!!

  • 不要直接继承 QThread: 尽量避免直接继承 QThread 类并重写 run() 函数的方式来创建线程。虽然这种方式在某些情况下可行,但容易导致代码的混乱和不必要的复杂性。推荐的方式是创建一个普通的 QObject 子类,并在其内部创建 QThread 对象,然后将需要在新线程中执行的任务放在这个 QObject 的槽函数中。

  • 信号与槽跨线程连接: 当在不同线程中的对象之间进行信号与槽的连接时,确保使用 Qt 提供的线程安全的连接方式,即使用 Qt::QueuedConnection 或 Qt::AutoConnection 参数。这样可以确保信号的发送和槽的执行都在接收者对象所在的线程中进行,避免线程安全问题。

  • 避免直接操作 GUI: 如果在工作线程中需要更新 GUI,避免直接在工作线程中操作 GUI 控件,因为 Qt GUI 是线程不安全的。可以通过信号与槽机制将更新 GUI 的任务发送到主线程中执行,或者使用 QMetaObject::invokeMethod() 函数来跨线程调用 GUI 更新函数。

  • 正确管理线程生命周期: 确保正确管理线程的生命周期,包括线程的启动、暂停、终止等操作。在启动线程之前,确保线程对象的相关属性已经设置好,如优先级、栈大小等。在线程执行完毕后,及时释放线程对象和相关资源。

  • 避免死锁和竞态条件: 在多线程编程中,特别是涉及到共享资源的情况下,要格外注意避免死锁和竞态条件的发生。合理使用互斥锁、条件变量等同步机制,确保线程安全性。

  • 异常处理: 在线程中可能会发生异常,确保在合适的地方捕获和处理异常,以避免线程意外终止或程序崩溃。

  • 测试与调试: 多线程程序的测试和调试相对复杂,需要注意并发执行时的各种可能情况。使用工具和技术进行测试,并尽早发现和解决潜在的并发问题。

  • 性能优化: 合理利用多线程可以提高程序的性能,但过多的线程可能会导致线程切换和资源竞争,降低性能。因此,需要根据实际情况优化线程数量和任务分配,以达到最佳性能。

上大学那会反反复复学了好多次都没有给整明白线程是咋使用的,怎么去实现并发,使用例如互斥锁(Mutex)、条件变量(Condition Variable)、信号量(Semaphore)等同步机制去管理资源,现在工作两年多了也不太会去使用这些优化代码,提高程序运行效率,可能是用的不多,学了很多遍都没记得住,学完还是得记录下来,方便之后不记得了回过头来看。

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值