前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Qt官方示例-信号量

Qt官方示例-信号量

作者头像
Qt君
发布2023-03-17 13:35:08
7260
发布2023-03-17 13:35:08
举报
文章被收录于专栏:跟Qt君学编程跟Qt君学编程

❝演示Qt多线程的信号量操作编程。❞

  生产者将数据写入缓冲区,直到到达缓冲区末尾为止,然后从头开始重新开始,覆盖现有数据。使用者线程读取生成的数据,并将其写入标准错误。

  信号量比互斥量可以具有更高的并发级别。如果对缓冲区的访问由QMutex保护,则使用者线程无法与生产者线程同时访问缓冲区。但是,使两个线程同时在缓冲区的不同部分上工作并没有什么害处。

  该示例包括两个类:ProducerConsumer。两者都继承自QThread。用于在这两个类之间进行通信的循环缓冲区以及保护它的信号量是全局变量。

  使用QSemaphore解决生产者-消费者问题的替代方法是使用QWaitConditionQMutex。如需看更多请查看Qt的"Wait Conditions Example"示例。

全局变量

  让我们从回顾循环缓冲区和相关的信号量开始:

代码语言:javascript
复制
const int DataSize = 100000;

const int BufferSize = 8192;
char buffer[BufferSize];

QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;

DataSize是生产者将生成的大量数据。为了使示例尽可能简单,我们将其设为常量。BufferSize是循环缓冲区的大小。它小于DataSize,表示生产者将在某个时候到达缓冲区的末尾并从头开始重新启动。

  为了使生产者和消费者同步,我们需要两个信号量。该freeBytes信号控制缓冲的"自由"区域(该区域的生产者还没有装满数据或消费者已经读取了)。useBytes信号量控制缓冲区的"已用"区域(生产者已填充但使用者尚未读取的区域)。

  信号量共同确保了生产者在使用方之前不会超过BufferSize字节,并且确保使用方从未读取过生产者尚未生成的数据。

freeBytes信号量使用BufferSize初始化,因为最初整个缓冲区为空。useBytes信号量初始化为0(如果未指定默认值)。

生产者类

  让我们回顾一下Producer该类的代码:

代码语言:javascript
复制
class Producer : public QThread
{
public:
    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            freeBytes.acquire();
            buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];
            usedBytes.release();
        }
    }
};

  生产者生成DataSize字节数据。在将字节写入循环缓冲区之前,它必须使用freeBytes信号量获取"空闲"字节。如果消费者没有跟上生产者的步伐,调用QSemaphore::acquire()将会在这里阻塞。

  最后,生产者使用usedBytes信号量释放一个字节。"空闲"字节已成功转换为"已使用"字节,供消费者读取。

消费者类

  现在转到Consumer类:

代码语言:javascript
复制
class Consumer : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            usedBytes.acquire();
            fprintf(stderr, "%c", buffer[i % BufferSize]);
            freeBytes.release();
        }
        fprintf(stderr, "\n");
    }
};

现在转到Consumer类:

代码语言:javascript
复制
class Consumer : public QThread
{
    Q_OBJECT
public:
    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            usedBytes.acquire();
            fprintf(stderr, "%c", buffer[i % BufferSize]);
            freeBytes.release();
        }
        fprintf(stderr, "\n");
    }
};

  该代码与生产者非常相似,除了这次我们获得一个"已用"字节并释放一个"空闲"字节,而不是相反。

main函数

  在main函数中,我们创建两个线程并调用QThread::wait()以确保两个线程在退出之前都有时间完成:

代码语言:javascript
复制
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    Producer producer;
    Consumer consumer;
    producer.start();
    consumer.start();
    producer.wait();
    consumer.wait();
    return 0;
}

  那么当我们运行程序时会发生什么呢?最初,生产者线程是唯一可以做任何事情的线程。消费者被阻止等待usedBytes信号量被释放(其初始available()计数为0)。一旦生产者将一个字节放入缓冲区中,freeBytes.available()BufferSize-1usedBytes.available()等于1。此时,可能会发生两件事:消费者线程接管并读取该字节,或者生产者线程获得第二个字节。

  本示例中提供的生产者-消费者模型使编写高度并发的多线程应用程序成为可能。在多处理器计算机上,该程序的运行速度可能是等效的基于互斥锁的程序的两倍,因为两个线程可以同时在缓冲区的不同部分处于活动状态。

「请注意」,尽管并非总是能实现这些好处的。获取和释放 QSemaphore 需要花费成本。实际上,将缓冲区划分为块并操作块而不是单个字节可能是值得的。缓冲区大小也是必须根据实验仔细选择其参数。

关于更多

  • 「QtCreator软件」可以找到:
  • 或在以下「Qt安装目录」找到:
代码语言:javascript
复制
C:\Qt\{你的Qt版本}\Examples\{你的Qt版本}\corelib\threads\semaphores
  • 「相关链接」
代码语言:javascript
复制
https://doc.qt.io/qt-5/qtcore-threads-semaphores-example.html
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Qt君 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 全局变量
  • 生产者类
  • 消费者类
  • main函数
  • 关于更多
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档