QT的互斥量和信号量

文章目录

一、mutex互斥量

1、mutex

目的是保护对象、数据结构或代码段,以便一次只有一个线程可以访问它。

c 复制代码
QMutex mutex;
int number = 6;

void method1()
{
    mutex.lock();
    number *= 5;
    number /= 4;
    mutex.unlock();
}

void method2()
{
    mutex.lock();
    number *= 3;
    number /= 2;
    mutex.unlock();
}

2、相关成员函数

c 复制代码
QMutex::QMutex(QMutex::RecursionMode mode)
//构造一个新的互斥对象。互斥锁是在未锁定状态下创建的。
//如果模式是QMutex::Recursive,则线程可以多次锁定同一个互斥对象,
//并且在进行相应数量的unlock()调用之前,该互斥对象不会被解锁。
//否则,线程可能只锁定一次互斥对象。默认值为QMutex::NonRecursive。
//递归互斥量比非递归互斥量慢,占用的内存也更多。
bool QMutex::isRecursive() const
//如果这个互斥量为递归返回true.
void QMutex::lock()
//锁定互斥对象。如果另一个线程锁定了互斥锁,那么这个调用将被阻塞,直到该线程将其解锁。
//如果该互斥对象是递归互斥对象,则允许在同一线程的同一互斥对象上多次调用该函数。
//如果这个互斥对象是非递归互斥对象,那么当互斥对象被递归锁定时,这个函数将死锁定。
bool QMutex::try_lock()
//尝试锁定互斥对象。如果已获得锁,此函数将返回true;否则返回false。
//提供此功能是为了与标准库概念Lockable兼容。它相当于tryLock()。

bool QMutex::tryLock(int timeout = 0)
//尝试锁定互斥对象。如果已获得锁,此函数将返回true;否则返回false。
//如果另一个线程锁定了互斥锁,则此函数最多会等待超时毫秒,以便互斥锁可用。
template <typename Rep, typename Period> bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration)
//尝试锁定互斥对象。如果已获得锁,此函数将返回true;否则返回false。
//如果另一个线程锁定了互斥锁,此函数将等待duration时间,以使互斥锁可用。
//注意:传递一个负的持续时间作为持续时间相当于调用try_lock()。此行为与tryLock()不同。
template <typename Clock, typename Duration> bool QMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint)
//尝试锁定互斥对象。如果已获得锁,此函数将返回true;否则返回false。
//如果另一个线程锁定了互斥锁,此函数将等待timepoint时间,以使互斥锁可用。
//注意:传递一个timepoint作为持续时间相当于调用try_lock()。此行为与tryLock()不同。
以上三个方法
//如果获得了锁,则必须使用unlock()解锁互斥锁,然后另一个线程才能成功锁定它。
//如果该互斥对象是递归互斥对象,则允许在同一线程的同一互斥对象上多次调用该函数。
//如果这个互斥锁是非递归互斥锁,那么当试图递归锁定互斥锁时,这个函数总是返回false。
void QMutex::unlock()
//解锁互斥锁。试图解锁与锁定互斥锁的线程不同的线程中的互斥锁会导致错误。
//解锁未锁定的互斥对象会导致未定义的行为。

二、semaphore信号量

1、信号量

信号量是互斥锁的一种推广。信号量通常用于保护一定数量的相同资源。信号量支持两种基本的操作,acquire()release() 。**acquire(n)**尝试获取n个资源,如果没有那么多可用资源,那么调用将被阻止,直到可以获取到n个资源;release(n) 释放n个资源。

c 复制代码
QSemaphore sem(5);      // sem.available() == 5

sem.acquire(3);         // sem.available() == 2
sem.acquire(2);         // sem.available() == 0
sem.release(5);         // sem.available() == 5
sem.release(5);         // sem.available() == 10,此时资源数超过初始值

sem.tryAcquire(1);      // sem.available() == 9, returns true
sem.tryAcquire(250);    // sem.available() == 9, returns false

2、成员函数

c 复制代码
QSemaphore::QSemaphore(int n = 0)
//创建新的信号量并且初始化资源的数量,资源视为n,默认为0;
void QSemaphore::acquire(int n = 1)
//尝试获取信号量保护的n个资源。如果n>available(),则此调用将阻塞,直到有足够的资源可用为止。
int QSemaphore::available() const
//返回信号量当前可用的资源数。这个数字永远不能是负数。
void QSemaphore::release(int n = 1)
//释放n个信号量保护的资源
bool QSemaphore::tryAcquire(int n = 1)
//尝试获取n个信号量保护的资源,如果获取到返回为true,
//如果 available() < n,则立即返回false,不会调用任何资源
bool QSemaphore::tryAcquire(int n, int timeout)
//尝试获取n个信号量保护的资源,如果获取到返回为true,
//如果available()<n,则此调用最多将等待超时毫秒,以便资源变为可用。
//注意:传递一个负数作为超时相当于调用acquire(),即如果超时为负数,此函数将永远等待资源可用。

三、Linux内核中的互斥锁、读写锁、自旋锁、信号量

1、互斥锁(mutex) 是最常用的锁,它可以保护共享资源,使得在某个时刻只有一个线程或进程可以访问它。读写锁(rwlock)则可以同时允许多个线程或进程读取共享资源,但只允许一个线程或进程写入它。自旋锁(spinlock)可以用来保护共享资源,使得在某个时刻只有一个线程或进程可以访问它,但它会使线程或进程"自旋",直到获得锁为止。最后,信号量(semaphore)可以用来控制对共享资源的访问,以保证其他线程或进程可以安全地访问它们。

2、读写锁(rwlock) 是一种用于控制多线程访问共享资源的同步机制。当一个线程需要读取共享资源时,可以获取读取锁,这样其他线程就可以同时读取该资源,而不会引发冲突。当一个线程需要写入共享资源时,可以获取写入锁,这样其他线程就不能访问该资源,从而保证数据的完整性和一致性。

3、自旋锁(spinlock) 是一种简单而有效的用于解决多线程同步问题的锁。它是一种排他锁,可以在多线程环境下保护共享资源,以防止多个线程同时对该资源进行访问。自旋锁的基本原理是,当一个线程试图获取锁时,它会不断尝试获取锁,直到成功为止。在这期间,线程不会进入休眠状态,而是一直处于忙等待(busy-waiting)状态,这也就是自旋锁的由来。

4、信号量(semaphore) 是一种常用的同步机制,它可以用来控制多个线程对共享资源的访问。它有助于确保同一时间只有一个线程能够访问共享资源,从而避免资源冲突和竞争。信号量是一种整数计数器,用于跟踪可用资源的数量。当一个线程需要访问共享资源时,它首先必须获取信号量,这会将信号量的计数器减少1,而当它完成访问共享资源后,它必须释放信号量,以便其他线程也可以访问共享资源。
更多详细介绍

四、QT简单日志类代码

1、头文件

c 复制代码
#ifndef LOG_H
#define LOG_H
#include <QDebug>
#include <QDate>
#include <QFile>
#include <QThread>
#include <QList>
#include <QSemaphore>
#include <QMutex>
/***************************************
* 说明:日志功能的单线程实现
* 作者:caokexiang
* 时间:20240522
******************************************/
class QSimpleLog : public QThread
{
    Q_OBJECT
public:
    QSimpleLog(QString const& fileName);
public:
/***************************************
* 说明: 将记录写入日志文件(写入中文需要提前使用QString::fromLocal8Bit转变字符格式)
* 参数: msg:待写入的信息
* 作者:caokexiang
* 时间:20240522
******************************************/
    void write(const QString& msg);
    virtual void run();
private:
    QString const fileName; // 文件名
    QList<QString> m_msg;   //写入线程的消息队列
    QMutex m_mutex;         //对m_msg进行线程安全保护的互斥信号量
    QSemaphore m_synSem;    //同步信号量,当消息队列未进入时,线程处于阻塞状态,可以避免while一直死循环
};
#endif

2、源文件

c 复制代码
#include "QSimpleLog.h"


QSimpleLog::QSimpleLog(QString const &fileName) : m_synSem(0), fileName(fileName){//初始化信号量,初始资源为0
} 

void QSimpleLog::write(const QString& msg)
{
    m_mutex.lock();
    QString currentDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd");
    QString message = QString("[%1]: %2").arg(currentDateTime).arg(msg);
    m_msg.push_back(message);
    m_mutex.unlock();
    m_synSem.release(); //资源数增加1
}

void QSimpleLog::run()
{
    while (true){
        m_synSem.acquire();
        m_mutex.lock();
        if (m_msg.isEmpty()){
            continue;
        }
        QString message = m_msg.front();
        m_msg.pop_front();
        QFile file(fileName);
        file.open(QIODevice::WriteOnly | QIODevice::Append);
        QTextStream text_stream(&file);
        text_stream << message << "\r\n";
        file.flush();
        file.close();
        m_mutex.unlock();
    }
}
相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能15 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G15 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt