QSemaphore介绍
QSemaphore 是 Qt 框架中提供的一个用于多个线程同步和数据存取的信号量类。与互斥锁(QMutex)和条件变量(QWaitCondition)一样,QSemaphore 也是一个跨平台通用的同步机制。
QSemaphore 类提供了一种通用计数信号量。信号量是互斥锁的一种泛化应用,与互斥锁不同的是,信号量可以被多次获取。信号量通常用于保护一定数量的相同资源,而且这些资源可以被多个线程共享
QSemaphore 更加灵活,它并不需要像互斥锁那样使用 lock() 和 unlock()、tryLock() 等方法来实现加锁解锁操作。QSemaphore 主要通过其 count() 方法控制在某时刻同时运行的等待线程数量和可执行的线程数量,可以用来实现资源池管理、限流等功能。
QSemaphore(int n = 0)中的n,是指允许访问被保护资源的最大并发线程数。QSemaphore 类中的信号量实际上是一个计数器,它用于记录能够同时访问某个共享资源的线程数。在 QSemaphore 中使用 acquire() 函数获取该信号量时,计数器会减 1;当使用 release() 函数释放该信号量时,计数器会加 1。因此,如果将 size 指定为 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
QSemaphore代码示例
下面代码定义了生产者和消费者两类线程,模拟了一个资源池管理的场景。在生产者线程中通过 releaseResource() 方法放入共享资源,而在消费者线程中通过 getResource() 方法获取它们。为了防止多个线程同时访问同一份资源,我们使用了互斥锁(QMutex)进行加锁解锁操作,并结合信号量(QSemaphore)进行资源数量的限制,从而确保了程序可以安全地并发运行。
pool.h
#ifndef POOL_H
#define POOL_H
#include <QObject>
#include <QtCore/QThread>
#include <QtCore/QSemaphore>
#include <QtCore/QDebug>
#include <QMutex>
class Pool : public QObject
{
Q_OBJECT
public:
Pool(int size):m_size(size) {
m_resourcesAvailable = new QSemaphore(size); //size是初始资源数量
}
void getResource() {
m_resourcesAvailable->acquire(); // 请求获取信号量 , 阻塞直到有可用的资源
qDebug() << "getResource current thread id : " << QThread::currentThreadId();
m_mutex.lock(); // 加锁
QString resource = m_resources.takeFirst();
m_mutex.unlock(); // 解锁
qDebug() << "getResource get resource : " << resource;
}
void releaseResource(QString res) {
m_mutex.lock(); // 加锁
m_resources.append(res);
m_mutex.unlock(); // 解锁
m_resourcesAvailable->release(); // 释放信号量,资源数+1
qDebug() << "releaseResource current thread id : "<< QThread::currentThreadId() << ", Remaining num :" << m_resourcesAvailable->available();
}
private:
QList<QString> m_resources{"ResourceA", "ResourceB", "ResourceC", "ResourceD", "ResourceE"};
int m_size;
QMutex m_mutex; // QMutex 提供资源独占的能力。
QSemaphore *m_resourcesAvailable; // 信号量,可用资源数量
};
#endif // POOL_H
threadmanager.h
#ifndef THREADMANAGER_H
#define THREADMANAGER_H
#include <QtCore/QThread>
#include <QtCore/QSemaphore>
#include <QtCore/QDebug>
#include <QMutex>
#include "pool.h"
class Consumer : public QThread {
Q_OBJECT
public:
Consumer(Pool *pool) : m_pool(pool) {}
~Consumer() override {}
void run() override {
for (int i = 0; i < 3; ++i) {
qDebug() << "Consuming at" << QThread::currentThreadId();
m_pool->getResource();
msleep(500);
}
}
private:
Pool *m_pool;
};
class Producer : public QThread {
Q_OBJECT
public:
Producer(Pool *pool, QString resource) : m_pool(pool), m_resource(resource) {}
~Producer() override {}
void run() override {
for (int i = 0; i < 2; ++i) {
qDebug() << "Producing at" << QThread::currentThreadId();
m_pool->releaseResource(m_resource.arg(i));
msleep(1000);
}
}
private:
Pool *m_pool;
QString m_resource;
};
#endif // THREADMANAGER_H
main.cpp
#include <QCoreApplication>
#include "pool.h"
#include "threadmanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Pool pool(5); // 共享资源池
Consumer consumer1(&pool), consumer2(&pool), consumer3(&pool);
Producer producer1(&pool, "Producer1 Resource %1"), producer2(&pool, "Producer2 Resource %1");
producer1.start();
producer2.start();
consumer1.start();
consumer2.start();
consumer3.start();
consumer1.wait();
consumer2.wait();
consumer3.wait();
producer1.wait();
producer2.wait();
return a.exec();
}
运行结果:
Producing at 0x7f373f062700
releaseResource current thread id : 0x7f373f062700 , Remaining num : 6
Producing at 0x7f373e861700
releaseResource current thread id : 0x7f373e861700 , Remaining num : 7
Consuming at 0x7f373e060700
getResource current thread id : 0x7f373e060700
getResource get resource : "ResourceA"
Consuming at 0x7f373d85f700
getResource current thread id : 0x7f373d85f700
getResource get resource : "ResourceB"
Consuming at 0x7f373d05e700
getResource current thread id : 0x7f373d05e700
getResource get resource : "ResourceC"
Consuming at 0x7f373d05e700
getResource current thread id : 0x7f373d05e700
getResource get resource : "ResourceD"
Consuming at 0x7f373d85f700
getResource current thread id : 0x7f373d85f700
getResource get resource : "ResourceE"
Consuming at 0x7f373e060700
getResource current thread id : 0x7f373e060700
getResource get resource : "Producer1 Resource 0"
Producing at 0x7f373e861700
releaseResource current thread id : 0x7f373e861700 , Remaining num : 2
Producing at 0x7f373f062700
releaseResource current thread id : 0x7f373f062700 , Remaining num : 3
Consuming at 0x7f373e060700
getResource current thread id : 0x7f373e060700
getResource get resource : "Producer2 Resource 0"
Consuming at 0x7f373d85f700
getResource current thread id : 0x7f373d85f700
getResource get resource : "Producer2 Resource 1"
Consuming at 0x7f373d05e700
getResource current thread id : 0x7f373d05e700
getResource get resource : "Producer1 Resource 1"