QT的互斥量和信号量

一、mutex互斥量

1、mutex

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

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、相关成员函数

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个资源。

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、成员函数

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、头文件

#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、源文件

#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();
    }
}
  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值