• Qt——多线程


    一、QThread

            如果要设计多线程程序,一般是从QThread继承定义一个线程类,并重新定义QThread的虚函数 run() ,在函数 run() 里处理线程的事件循环。

            应用程序的线程称为主线程,创建的其他线程称为工作线程。主线程的 start() 函数会在其内部调用 run() 函数,然后 exec() 函数会让 run() 函数保持运行,进入工作线程的事件循环,随时接收系统事件。如下图所示:

    注:MyThread 继承于 QThread。

    二、线程同步

    1. 互斥量(Mutex)

            在多线程程序中,线程之间可能需要访问同一个变量(临界资源),或一个线程需要等待另一个线程完成某个操作才产生相应的动作。QMutexQMutexLocker 都是基于互斥量的线程同步类。

            QMutex lock() 函数用来锁定互斥量,unlock() 函数用来解锁互斥量。这两个方法必须配对使用。其次还要一个 tryLock() 函数,如果函数返回 true 则表示成拿到了共享资源。示例如下:

    1. QMutex mutex;
    2. void MyThread::run() {
    3. while(true) {
    4. mutex.lock(); //锁定互斥量
    5. ...
    6. mutex.unlock(); //解锁互斥量
    7. }
    8. }

             QMutexLocker 和 QMutex 相比,是一个简化了互斥量处理的类。它的构造函数接受互斥量作为参数并将其锁定,其析构函数则将此互斥量解锁。QMutexLocker 会使其在生存期内的代码片段得到保护。示例如下:

    1. QMutex mutex;
    2. void MyThread::run() {
    3. while(true) {
    4. if (mutex.tryLock(500)) //尝试锁定一个信号量,最多等500ms
    5. {
    6. QMutexLocker locker(&mutex); //锁定mutex,超出if范围自动解锁
    7. ...
    8. }
    9. }
    10. }

    2. 读写锁(ReadWriteLock)

            使用互斥量时存在一个问题,那就是每次只有一个线程获得互斥量的使用权限。如果在一个程序中有多个线程读取某个变量,那么这个变量就不是临界资源,是允许同时访问的。这时我们就需要 Qt 提供的读写锁类 QReadWriteLock,它是基于读或写的方式进行代码片锁定的。

            其中 lockForRead() 函数会以只读方式锁定资源,锁定后其他线程只能读不能写。 lockForWrite() 函数以写的方式锁定资源,锁定后其他线程不能读也不能写。

    3. 条件等待(WaitCondition)

            QWaitCondition 提供了一种改进的线程同步方法。它通过与 QReadWriteLock 结合使用,可以使一个线程在满足一定条件下通知其他多个线程,使其他多个线程即时进行响应。

            其中 wait() 函数用于释放互斥量,wakeAll() 函数用于唤醒所有处于等待状态的线程。wait() 函数定义如下:

    bool wait(QReadWriteLock *readWriteLock, unsigned long time)

            QWaitCondition 一般用于生产者/消费者(producer/consumer)问题。暂且假设缓冲区一直有资源且无限大,示例代码如下:

    1. QReadWriteLock rwLocker; //读写锁
    2. QWaitCondition waiter; //控制线程同步的对象
    3. // 生产者
    4. void ProducerThread::run() {
    5. while(1) {
    6. rwLocker.lockForWrite(); //以写方式锁定
    7. ...
    8. rwLocker.unlock(); //解锁
    9. waiter.wakeAll(); //唤醒其他等待的线程
    10. msleep(500); //线程休眠500ms
    11. }
    12. }
    13. // 消费者1
    14. void ConsumerThread1::run() {
    15. while(1) {
    16. rwLocker.lockForRead(); //以只读方式锁定
    17. waiter.wait(&rwLocker); //等待被唤醒
    18. ...
    19. rwLocker.unlock(); //解锁
    20. }
    21. }
    22. // 消费者2
    23. void ConsumerThread2::run() {
    24. while(1) {
    25. rwLocker.lockForRead(); //以只读方式锁定
    26. waiter.wait(&rwLocker); //等待被唤醒
    27. ...
    28. rwLocker.unlock(); //解锁
    29. }
    30. }

    4. 信号量(Semaphore)

             信号量与互斥量类似,但二者又有区别。一个互斥量只能被锁定一次,而信号量可以被多次利用。信号量通常用来保护一定数量的相同资源,如双缓冲区。

            QSemaphore 就是 Qt 实现信号量功能的类。其中 acquire(int n) 用于尝试获得n个资源,release(int n) 用于释放n个资源。available() 用于返回当前信号量的可用资源个数。

  • 相关阅读:
    0. HarmonyOS开发环境搭建问题
    Go语言中入门Hello World以及IDE介绍
    浅谈 kafka
    记一次 .NET 某药厂业务系统 CPU爆高分析
    [Linux入门]---Linux指令②
    【MySQL】ibd2sdi工具介绍和使用
    【JVM虚拟机】JVM常见面试题总结
    ConcurrentDictionary<T,V> 的这两个操作不是原子性的
    简单描述下微信小程序的目录结构
    华南技术栈CNN+Bilstm+Attention
  • 原文地址:https://blog.csdn.net/weixin_51418964/article/details/141088041