QT之使用 QWaitCondition 同步线程小例子

        接上一篇,本篇文章主要将介绍如何使用 QWaitCondition 来同步线程。

        学习 QWaitCondition 类之间,先研读下 Qt 文档对 QwaitCondition 的具体描述及介绍,大意如下:

        QWaitCondition 类提供了一个条件变量用于同步线程。

        QWaitCondition 允许一个线程告诉其他线程某些条件已被满足。一个或多个线程可以阻塞等待一个由 QWaitCondition 设定的条件 WakeOne() 或 WakeAll()。使用 WakeOne() 去唤醒一个随意选择的线程或者使用 WakeAll() 去唤醒他们所有。

        例如,假定当用于按下一个键我们有3条任务应该被执行,每一个任务都可以被分成一个线程,每一个都有一个 run() 主体,就像下面这样:

forever {
    mutex.lock();
    keyPressed.wait(&mutex);
    do_something();
    mutex.unlock();
}
        在这里,keyPressed 是一个 QWaitCondition 类型的全局变量。

        第4条线程可以读取键的按压,每次接受一次,就会唤醒其他3条线程,就像下面这样:

forever {
    getchar();
    keyPressed.wakeAll();
}
        这3条线程唤醒顺序是未定义的。另外,如果当键被按压仍有一些线程在执行 do_something() ,那么,键按压之后它们将不会被唤醒(因为它们没有等待条件变量)并且任务也不会去执行。这个问题可以使用计数器和 QMutex 去防护解决它。例如,这里有个新的代码用于工作线程:

forever {
    mutex.lock();
    keyPressed.wait(&mutex);
    ++count;
    mutex.unlock();

    do_something();

    mutex.lock();
    --count;
    mutex.unlock();
}
        这里有个代码是用于第4条线程的:

forever {
    getchar();

    mutex.lock();
    // Sleep until there are no busy worker threads
    while (count > 0) 
    {
        mutex.unlock();
        sleep(1);
        mutex.lock();
    }
    keyPressed.wakeAll();
    mutex.unlock();
}
        互斥量是必须的,因为两条线程尝试同时修改相同变量的值,其结果是不可预知的。

        原始的同步线程中使用等待条件是强而有力的,Wait Conditions Example 这个例子显示了如何使用 QWaitCondition 替代 QSemaphore 去控制访问环形共享内存通过生产者线程和消费者线程。


        好吧,到此为止,我只是把 Qt 帮助文档翻译了一遍。


        现在上 Qt 自带样例代码:

【Producer.h】

#ifndef PRODUCER_H
#define PRODUCER_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <iostream>

extern QMutex mutex;
extern const int DataSize;
extern const int BufferSize;
extern char buffer[8];
extern int numUsedBytes;
extern QWaitCondition bufferNotEmpty;
extern QWaitCondition bufferNotFull;

class Producer : public QThread
{
public:
    explicit Producer(QObject* parent = NULL);

    virtual void run();
};

#endif // PRODUCER_H
【Producer.cpp】

#include "producer.h"
#include <QTime>

Producer::Producer(QObject* parent) : QThread(parent)
{

}


void Producer::run()
{
    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));

    for(int i = 0; i < DataSize; i++)
    {
        /*
         * QMutex: The purpose of a QMutex is to protect an object,
         * data structure or section of code so that only one thread can
         * access it at a time
         * QMutex可以抱回一个对象, 数据结构, 以及代码段在同一时刻只能有一条线程访问
         *
         * 锁住, 保护对变量的访问, 每次只有一条线程能访问
        */
        mutex.lock();

        //如果可用缓冲区是满的, 则堵塞当前线程, 等待消费者线程读取
        if(numUsedBytes == BufferSize)
        {
            /*
             * 释放互斥量, 并阻塞线程等待条件
             * 解锁条件是: wakeOne() or wakeAll() 以及 timeout
            */
            bufferNotFull.wait(&mutex);
        }
        mutex.unlock();

        buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
        std::cout << buffer[i % BufferSize] << std::endl;

        //锁住变量 numUsedBytes
        mutex.lock();
        ++numUsedBytes;
        //通知生产者线程
        bufferNotEmpty.wakeAll();
        //解锁
        mutex.unlock();
    }
}


【Consumer.h】

#ifndef CONSUMER_H
#define CONSUMER_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <iostream>

extern QMutex mutex;
extern const int DataSize;
extern const int BufferSize;
extern char buffer[8];
extern int numUsedBytes;
extern QWaitCondition bufferNotEmpty;
extern QWaitCondition bufferNotFull;

class Consumer : public QThread
{
public:
    explicit Consumer(QObject* parent = NULL);

    virtual void run();

signals:
    void stringConsumed(const QString& text);
};

#endif // CONSUMER_H
【Consumer.cpp】

#include "consumer.h"

Consumer::Consumer(QObject* parent) : QThread(parent)
{

}

void Consumer::run()
{
    for (int i = 0; i < DataSize; ++i)
    {
        mutex.lock();
        if (numUsedBytes == 0)
        {
            //如果可用缓存区数据为0, 则通知生产则线程更新数据, 并堵塞当前线程等待条件
            bufferNotEmpty.wait(&mutex);
        }
        mutex.unlock();

        std::cout << buffer[i % BufferSize] << std::endl;

        //锁住并保护对变量的访问, 每次只有一条线程能访问
        mutex.lock();
        //清空计数
        --numUsedBytes;
        //唤醒生产者线程
        bufferNotFull.wakeAll();
        //解锁
        mutex.unlock();
     }
}


【main.cpp】

#include <QCoreApplication>
#include "producer.h"
#include "consumer.h"

const int DataSize = 24;
const int BufferSize = 1;
char buffer[8];
int numUsedBytes = 0;

QMutex mutex;
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Producer producer;
    Consumer consumer;
    producer.start();
    consumer.start();
    producer.wait();
    consumer.wait();

    return a.exec();
}

        执行结果:



       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

l357630798

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值