上一篇文章主要介绍了使用Windows API创建和使用信号量Semaphore
线程的互斥和同步(5)- Windows的信号量Semaphore
本篇文章只要介绍Qt的信号量 QSemaphore 类的使用,并用一个 生产者-消费者 实例来说明信号量是如何提高多线程的效率的。
下面是使用类 QSemaphore 常用的函数:
下面是一个 生产者-消费者 的使用示例说明:
在这个示例中,生产者向数组中写入数据;消费者从数组中读出数据,因为他们同时操作的不是同一块内存,因此同一时刻可以完全的并发执行。下面是一个示例图:
图画的不太好看,因为写入和读取的不是用一块内存,因此可以同时进行。
当数组缓存满了的时候,写入线程阻塞;当可用资源为空的时候,读取线程等待资源写入完成。
数据定义的代码如下:
#include <QSemaphore>
#include <atomic>
extern "C" int g_data[20]; // 数据缓存
extern "C" QSemaphore g_semaphore; // 信号量
extern "C" std::atomic<int> g_nStartIndex; // 读取数据的缓存起始位置
extern "C" std::atomic<int> g_nEndIndex; // 写入数据的缓存起始位置
extern "C" std::atomic<int> g_nCurrentCreated; // 当前缓存可用数目
这里定义了20个int类型的缓存数据。
全局变量的初始化,在cpp文件中
int g_data[20] = {-1};
QSemaphore g_semaphore(20);
std::atomic<int> g_nStartIndex(0);
std::atomic<int> g_nEndIndex(0);
std::atomic<int> g_nCurrentCreated(0);
生产者产生数据
生产者定义:
// 生产者
class ProducerThread : public CThread
{
public:
void run(void) override;
private:
int number = 0;
};
run函数为线程的入口函数,CThread 是自定义线程类
生产者的实现:
void ProducerThread::run(void)
{
while (1)
{
// 请求一个资源
g_semaphore.acquire(1);
// 生产数据
g_data[g_nEndIndex] = number;
std::cout << "Created Data: " << number++ \
<< ", Index is " << g_nEndIndex << std::endl;
g_nEndIndex++;
if (g_nEndIndex == 20)
g_nEndIndex = 0;
::Sleep(100);
g_nCurrentCreated++;
}
}
生产者每个100ms生产一个数据。当缓存满了的时候,
g_semaphore.acquire(1); 语句会处于阻塞的状态,直到有资源被释放。
消费者读取数据
消费者定义:
// 消费者
class ConsumerThread : public CThread
{
public:
void run(void) override;
};
消费者实现:
void ConsumerThread::run(void)
{
while (1)
{
// 自旋等待
while (g_nCurrentCreated <= 0);
// 消费数据
std::cout << "Read Value: " << g_data[g_nStartIndex] \
<< ", Index is " << g_nStartIndex << std::endl;
g_nStartIndex++;
if (g_nStartIndex == 20)
g_nStartIndex = 0;
::Sleep(2000);
// 释放资源
g_nCurrentCreated--;
g_semaphore.release();
}
}
这里消费者,使用函数 g_semaphore.release(); 释放资源。
消费者每隔2000ms释放一次资源。
调用部分:
// 创建生产者和消费者
ConsumerThread consumer;
ProducerThread producer;
consumer.start();
producer.start();
// 等待线程结束
consumer.wait();
producer.wait();
运行结果:
Created Data: 0, Index is 0
Read Value: 0, Index is 0
Created Data: 1, Index is 1
Created Data: 2, Index is 2
Created Data: 3, Index is 3
Created Data: 4, Index is 4
Created Data: 5, Index is 5
Created Data: 6, Index is 6
Created Data: 7, Index is 7
Created Data: 8, Index is 8
Created Data: 9, Index is 9
Created Data: 10, Index is 10
Created Data: 11, Index is 11
Created Data: 12, Index is 12
Created Data: 13, Index is 13
Created Data: 14, Index is 14
Created Data: 15, Index is 15
Created Data: 16, Index is 16
Created Data: 17, Index is 17
Created Data: 18, Index is 18
Created Data: 19, Index is 19
Read Value: 1, Index is 1
Created Data: 20, Index is 0
Read Value: 2, Index is 2
Created Data: 21, Index is 1
Read Value: 3, Index is 3
Created Data: 22, Index is 2
Read Value: 4, Index is 4
Created Data: 23, Index is 3
首先生产者创建了一个数据,消费数据。因为消费者消费数据比较慢,因此创建者很快就将缓存创建满了。
当消费者再次消费数据后,生产者马上创建数据。