• 【嵌入式——QT】线程同步


    概述

    代码段是希望被保护起来的,在执行过程中不被其他线程打断,以保证计算结果的完整性,这就是线程同步的概念。

    基于互斥量的线程同步

    QMutex

    • lock():锁定互斥量,如果另外一个线程锁定了这个互斥量,它将阻塞执行直到其他线程解锁这个互斥量;
    • unlock():解锁一个互斥量,需要与lock()配对使用;
    • tryLock():视图锁定一个互斥量,如果成功锁定就返回true,如果其他线程已经锁定了这个互斥量,就返回false,但不阻塞程序执行;

    QMutexLocker
    QMutexLocker是另外一个简化互斥量处理的类,QMutexLocker的构造函数接受一个互斥量作为参数并将其锁定,QMutexLocker的析构函数则将此互斥量解锁,所以在QMutexLocker实例变量的生存期内的代码段得到保护,自动进行互斥量的锁定和解锁;

    QMutex mutex;
    QMutexLocker Locker(&mutex);
    
    • 1
    • 2

    基于QReadWriteLock的线程同步

    使用互斥量存在一个问题,每次只能有一个线程获得互斥量的权限,如果在一个程序中有多个线程读取某个变量,使用互斥量时也必须排队,而实际上若只是读取一个变, 是可以让多个线程同时访问的,这样互斥量就会降低程序的性能。
    它是基于读或写的模式进行代码段锁定的,在多个线程读写一个共享数据时,可以解决上面所说的互斥量存在的问题,QReadWriteLock以读或写锁定的同步方法允许以读或写的方式保护一段代码,它可以允许多个线程以只读方式同步访问资源,但是只要有一个线程在以写方式访问资源时,其他线程就必须等待直到写操作结束。
    QReadLocker和QWriteLocker是QReadWriteLock简便形式。

    主要函数

    • lockForRead():以只读的方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞;
    • lockForWrite():以写入方式锁定资源,如果本线程或其他线程以读或写模式锁定资源,这个函数就阻塞;
    • unlock():解锁;
    • tryLockForRead():是lockForRead()非阻塞版本;
    • tryLockForWrite():是lockForWrite()非阻塞版本;

    代码示例
    ReadWriteThread.h

    #ifndef READWRITETHREAD_H
    #define READWRITETHREAD_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    
    class ReadWriteThread : public QThread
    {
        Q_OBJECT
    public:
        ReadWriteThread();
    
        void run() Q_DECL_OVERRIDE;
    
        QReadWriteLock lock;
    };
    
    #endif // READWRITETHREAD_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    ReadWriteThread.cpp

    #include "ReadWriteThread.h"
    
    ReadWriteThread::ReadWriteThread() {}
    
    // void ReadWriteThread::run()
    // {
    //     QWriteLocker lockW(&lock);
    //     lock.lockForWrite();
    //     //模拟线程1数据写入代码
    //     //。。。
    //     lock.unlock();
    // }
    
    
    // void ReadWriteThread::run()
    // {
    //     QReadLocker lockR(&lock);
    //     lock.lockForRead();
    //     //模拟线程2读取数据代码
    //     //。。。
    //     lock.unlock();
    // }
    
    
    
    void ReadWriteThread::run()
    {
        //QReadLocker lockR(&lock);
        lock.lockForRead();
        //模拟线程3读取数据代码
        //。。。
        lock.unlock();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    基于QWaitCondition的线程同步

    QWaitCondition提供了另外一种改进的线程同步方法,QWaitCondition与QMutex结合,可以使一个线程在满足一定条件时通知其他多个线程,使它们及时做出响应,这样比只使用互斥量效率要高一些。
    QWaitCondition一般用于生产者/消费者模型中,生产者产生数据,消费者消费数据。
    主要函数

    • wait(QMutex *lockedMutex, unsigned long time):解锁互斥量lockedMutex,并阻塞等待唤醒条件,被唤醒后锁定lockedMutex并退出函数;
    • wakeAll():唤醒所有处理等待状态的线程,线程唤醒的顺序不确定,由操作系统的调度策略决定;
    • wakeOne():唤醒一个处理等待状态的线程,唤醒哪个线程不确定,由操作系统的调度策略决定;
      代码示例
      QMyThread.h
    #ifndef QMYTHREAD_H
    #define QMYTHREAD_H
    #include 
    #include 
    #include 
    class QMyThread : public QThread
    {
    
    };
    
    class QThreadProducer : public QThread
    {
    
        Q_OBJECT
    
    public:
        QThreadProducer();
        void run() Q_DECL_OVERRIDE;
        void stopThread();
    
        bool m_stop=false;
    
    };
    
    class QThreadConsumer : public QThread
    {
        Q_OBJECT
    public:
        QThreadConsumer();
        void run() Q_DECL_OVERRIDE;
    
        void stopThread();
        bool m_stop=false;
    signals:
        void newValue(int seq, int diceValue);
    };
    
    
    
    
    #endif // QMYTHREAD_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    QMyThread.cpp

    #include "QMyThread.h"
    #include 
    int seq = 0;
    int diceValue = 0;
    
    QMutex mutex;
    QWaitCondition newdataAvailable;
    
    QThreadProducer::QThreadProducer() {}
    
    void QThreadProducer::run()
    {
        m_stop= false;
        seq = 0;
        qsrand(QTime::currentTime().msec());
        while(!m_stop) {
            mutex.lock();
            diceValue=qrand();
            diceValue = (diceValue % 6)+1;
            seq++;
            mutex.unlock();
            newdataAvailable.wakeAll();//唤醒所有线程
            msleep(500);
        }
    }
    
    void QThreadProducer::stopThread()
    {
        m_stop = true;
    }
    
    QThreadConsumer::QThreadConsumer() {}
    
    
    void QThreadConsumer::run()
    {
        m_stop= false;
        while(!m_stop) {
            mutex.lock();
            newdataAvailable.wait(&mutex);//先解锁mutex,使其他线程可以使用mutex
            emit newValue(seq, diceValue);
            mutex.unlock();
        }
    }
    
    void QThreadConsumer::stopThread()
    {
        m_stop = true;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    基于信号量的线程同步

    信号量(Semaphore)是另一种限制对共享资源进行访问的线程同步机制,它与互斥量(Mutex)相似,但是有区别,一个互斥量只能被锁一次,而信号量可以多次使用,信号量通常用来保护一定数量的相同的资源,如数据采集时的双缓冲区。

    主要函数

    • acquire(int n = 1):尝试获取n个资源,如果没有这么多资源,线程将阻塞直到有n个资源可用;
    • release(int n = 1):释放n个资源,如果信号量的资源已全部可用之后在release(),就可以创建更多的资源,增加可用资源的个数;
    • available() const:返回当前信号量可用的资源个数,这个数用于不可能为负数,如果为0,说明当前没有资源可用;
    • tryAcquire(int n = 1):尝试获取n个资源,不成功时不阻塞线程;

    代码示例
    SemaphoreThread.h

    #ifndef SEMAPHORETHREAD_H
    #define SEMAPHORETHREAD_H
    #include 
    
    class SemaphoreThread
    {
    
    };
    
    
    class QThreadDao : public QThread
    {
        Q_OBJECT
    public :
        QThreadDao();
        bool m_stop = false;
    
        void run() Q_DECL_OVERRIDE;
    
        void stopThread();
    
    };
    
    class QThreadShow : public QThread
    {
    
        Q_OBJECT
    public :
        QThreadShow();
        bool m_stop = false;
    
        void run() Q_DECL_OVERRIDE;
    
        void stopThread();
    
    
    signals:
        void newValue(int* data, int count, int seq);
    
    };
    
    #endif // SEMAPHORETHREAD_H
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    SemaphoreThread.cpp

    #include "SemaphoreThread.h"
    #include 
    
    const int BufferSize = 8;
    int buffer1[BufferSize];
    int buffer2[BufferSize];
    
    int curBuf =1;
    int bufNo =0;
    quint8 counter = 0;
    
    QSemaphore emptyBufs(2);//信号量,空的缓冲区个数,初始资源格式为2
    QSemaphore fullBufs;//满的缓冲区个数,初始资源为0
    
    QThreadDao::QThreadDao()
    {
    }
    
    void QThreadDao::run()
    {
        m_stop = false;
        bufNo = 0;//缓冲区序号
        curBuf =1;//当前写入使用的缓冲区
        counter = 0;//数据生成器
        int n = emptyBufs.available();
        if(n<2) {
            //保证线程启动的时候,emptyBufs.available =2
            emptyBufs.release(2-n);
        }
        while(!m_stop) {
            emptyBufs.acquire();//获取一个空的缓冲区
            for(int i=0; i<BufferSize; i++) {
                if(curBuf == 1) {
                    buffer1[i] = counter;//向缓冲区写入数据
                } else {
                    buffer2[i] = counter;//向缓冲区写入数据
                }
                counter++;
                msleep(50);
            }
            bufNo++;
            //切换当前写入缓冲区
            if(curBuf == 1) {
                curBuf =2;
            } else {
                curBuf =1;
            }
            fullBufs.release();
        }
    }
    
    void QThreadDao::stopThread()
    {
        m_stop = true;
    }
    
    QThreadShow::QThreadShow()
    {
    }
    
    void QThreadShow::run()
    {
        m_stop = false;
        int n = fullBufs.available();
        if(n>0) {
            fullBufs.acquire(n);
        }
        while(!m_stop) {
            fullBufs.acquire();//等待有缓冲区满当 fullBufs.available()=0 阻塞
            int bufferData[BufferSize];
            int seq = bufNo;
            if(curBuf==1) {
                //当前写入缓冲区是1,那么满的缓冲区是2
                for(int i=0; i<BufferSize; i++) {
                    bufferData[i] = buffer2[i];
                }
            } else {
                for(int i=0; i<BufferSize; i++) {
                    bufferData[i] = buffer1[i];
                }
            }
            emptyBufs.release();//释放一个空缓冲区
            emit newValue(bufferData, BufferSize, seq);
        }
    }
    
    void QThreadShow::stopThread()
    {
        m_stop = true;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
  • 相关阅读:
    JS,jQuery常用语法记录
    promise使用与源码封装(二)
    AI助力科研创新与效率双提升:ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写
    与六年测试工程师促膝长谈,他分享的这些让我对软件测试工作有了全新的认知~
    12.cuBLAS开发指南中文版--cuBLAS中的Level-1函数asum()和axpy()
    套接字属性设置
    如何构建最佳的SaaS联盟计划
    Ubuntu18.04 ROS-Melodic安装Moveit
    智能批量重命名,轻松删除文件名后缀数字并添加编号!
    MongoDB-索引-部分索引
  • 原文地址:https://blog.csdn.net/weixin_39723539/article/details/136736805