QWaitCondition源码解析(暨生产者消费者线程分析)

前言

此记录为刷leetcode 过程中复习Qt多线程的意外收获,也是对自己使用Qt过程中疑问的回答


一、官方自带生产者消费者源码分析

1、官方Wait Conditions Example源码

#include <QtCore>

#include <stdio.h>
#include <stdlib.h>

//! [0]
const int DataSize = 100000;

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

QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
//! [0]

//! [1]
class Producer : public QThread
//! [1] //! [2]
{
public:
    Producer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == BufferSize)
                bufferNotFull.wait(&mutex);
            mutex.unlock();

            buffer[i % BufferSize] = "ACGT"[QRandomGenerator::global()->bounded(4)];

            mutex.lock();
            ++numUsedBytes;
            bufferNotEmpty.wakeAll();
            mutex.unlock();
        }
    }
};
//! [2]

//! [3]
class Consumer : public QThread
//! [3] //! [4]
{
    Q_OBJECT
public:
    Consumer(QObject *parent = NULL) : QThread(parent)
    {
    }

    void run() override
    {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == 0)
                bufferNotEmpty.wait(&mutex);
            mutex.unlock();

            fprintf(stderr, "%c", buffer[i % BufferSize]);

            mutex.lock();
            --numUsedBytes;
            bufferNotFull.wakeAll();
            mutex.unlock();
        }
        fprintf(stderr, "\n");
    }

signals:
    void stringConsumed(const QString &text);
};
//! [4]


//! [5]
int main(int argc, char *argv[])
//! [5] //! [6]
{
    QCoreApplication app(argc, argv);
    Producer producer;
    Consumer consumer;
    producer.start();
    consumer.start();
    producer.wait();
    consumer.wait();
    return 0;
}
//! [6]

#include "waitconditions.moc"

这个demo的大体意思是:现在有8192个字节的一个大坑,生产者线程往里面填,消费者线程使劲的挖,谁快了就停下来等着对着,直到100000个字节丢完了为止。

二、疑问及源码解析

1、疑点:互斥量加锁不释放,难道不死锁么?

摘录部分消费者代码,疑问来了,条件变量bufferNotEmpty与互斥量到底什么关系,wait个啥子,互斥量加锁,不释放是个什么鬼,程序还如何继续往下执行呢?,代码如下:

  void run() override
   {
        for (int i = 0; i < DataSize; ++i) {
            mutex.lock();
            if (numUsedBytes == 0)
                bufferNotEmpty.wait(&mutex);		//条件变量bufferNotEmpty在此等待,互斥量mutex不释放,如何继续往下执行?
            mutex.unlock();

            fprintf(stderr, "%c", buffer[i % BufferSize]);

            mutex.lock();
            --numUsedBytes;
            bufferNotFull.wakeAll();
            mutex.unlock();
        }
        fprintf(stderr, "\n");
    }

打开Qt官网的帮助文档,得到解释如下:
bool QWaitCondition::wait(QMutex *lockedMutex, unsigned long time = ULONG_MAX)
Releases the lockedMutex and waits on the wait condition. The lockedMutex must be initially locked by the calling thread. If lockedMutex is not in a locked state, the behavior is undefined. If lockedMutex is a recursive mutex, this function returns immediately. The lockedMutex will be unlocked, and the calling thread will block until either of these conditions is met:
Another thread signals it using wakeOne() or wakeAll(). This function will return true in this case.
time milliseconds has elapsed. If time is ULONG_MAX (the default), then the wait will never timeout (the event must be signalled). This function will return false if the wait timed out.
The lockedMutex will be returned to the same locked state. This function is provided to allow the atomic transition from the locked state to the wait state.
画重点,释放锁,并在此等待条件变量;解释了上一个疑问,它把锁释放掉了,可以继续往下执行;但是既然把锁释放了,那 bufferNotEmpty.wait(&mutex;后面那接下来的mutex.unlock()又是个什么鬼,重复释放?这么大的库不至于犯这种问题吧。

2.疑点:Qt会重复unlock么?

想着源码也不会太复杂,那就看看源码了。

bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
{
    if (!readWriteLock)
        return false;
    auto previousState = readWriteLock->stateForWaitCondition();
    if (previousState == QReadWriteLock::Unlocked)
        return false;
    if (previousState == QReadWriteLock::RecursivelyLocked) {
        qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()");
        return false;
    }

    QWaitConditionEvent *wce = d->pre();
    readWriteLock->unlock();						//1、释放锁

    bool returnValue = d->wait(wce, time);			//2、这里面等待事件发生,这才是真正的wait,windows平台下是WaitForSingleObjectEx(), 以前搞MFC的东西这会用上了,得瑟下

    if (previousState == QReadWriteLock::LockedForWrite)	//3、等待结束,再次上锁
        readWriteLock->lockForWrite();
    else
        readWriteLock->lockForRead();
    d->post(wce, returnValue);

    return returnValue;
}

有了以上的源码,不用多解释了吧,它真的把锁释放掉了,不过换成了等待事件;从整个过程来说,先释放锁,后加锁,成对出现,对用户透明,又实现了等待过程;顺便贴上等待的部分的源码:

bool QWaitConditionPrivate::wait(QWaitConditionEvent *wce, unsigned long time)
{
    // wait for the event
    bool ret = false;
    switch (WaitForSingleObjectEx(wce->event, time, FALSE)) {	//windows平台下,或者搞过MFC的娃不用多说,直接msdn就好了
    default: break;

    case WAIT_OBJECT_0:
        ret = true;
        break;
    }
    return ret;
}

三、总结

QWaitCondition需要与QMutex配合使用;
源码比较简单,也很巧妙,值得学习;
有问题,先文档,再源码,最后百度(没法google);
希望自己后面还有时间扣扣源码,记录点滴,在这条路上越走越远。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多线程编程中,生产者-消费者问题是一个经典的同步问题。在生产者-消费者问题中,生产者和消费者通过一个共享的缓冲区进行通信和数据交换。 QWaitConditionQt框架提供的一个用于线程同步的类,它通过条件变量实现了线程的等待和唤醒操作。在生产者-消费者问题中,可以利用QWaitCondition来实现线程间的同步和互斥。 生产者-消费者问题可以理解为一个生产者不断地向缓冲区中生产数据,而消费者则不断地从缓冲区中消费数据。当缓冲区满时,生产者需要等待消费者消费数据;当缓冲区空时,消费者需要等待生产生产数据。这就需要使用互斥锁和条件变量来实现线程的等待和唤醒操作。 在Qt中,可以使用QMutex和QWaitCondition来实现生产者-消费者问题。生产者和消费者共享一个缓冲区,使用QMutex进行互斥操作来保证只有一个线程能够访问缓冲区。当缓冲区满时,生产者调用QWaitConditionwait()函数进行等待;当缓冲区非空时,消费者调用QWaitCondition的wakeOne()或wakeAll()函数进行唤醒操作。 通过使用QWaitCondition,能够有效地实现生产者-消费者问题的线程同步和互斥。生产者和消费者线程可以通过条件变量进行等待和唤醒操作,从而保证线程间的顺序执行和数据的正确性。同时,QWaitCondition的使用也能够提高线程的效率和性能,避免了线程的空轮询和资源的浪费。 总而言之,QWaitConditionQt框架中用于线程同步的一个重要类,可以很好地解决生产者-消费者问题。通过使用QWaitCondition,能够实现生产者和消费者线程之间的通信和数据交换,实现线程的同步和互斥操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值