1. C++ queue 隊(duì)列基本用法
在C++中,queue是一個(gè)模板類(lèi),用于實(shí)現(xiàn)隊(duì)列數(shù)據(jù)結(jié)構(gòu),遵循先進(jìn)先出的原則。
? 常用方法: ·
queue<int> Q; //定義一個(gè)int型隊(duì)列
Q.empty(); //返回隊(duì)列是否為空
Q.size(); //返回當(dāng)前隊(duì)列長(zhǎng)度
Q.front(); //返回當(dāng)前隊(duì)列的第一個(gè)元素
Q.back(); //返回當(dāng)前隊(duì)列的最后一個(gè)元素
Q.push(); //在隊(duì)列后面插入一個(gè)元素, 比如插入數(shù)字5: Q.push(5)
Q.pop(); //從當(dāng)前隊(duì)列里,移出第一個(gè)元素
? 簡(jiǎn)單使用: ·
#include <iostream>
#include <queue>
using namespace std;
int main()
{
// 創(chuàng)建一個(gè)queue對(duì)象
queue<int> Q;
// 向隊(duì)列中添加元素
Q.push(1);
Q.push(2);
Q.push(3);
cout<<"queue empty? "<<Q.empty()<<endl;
cout<<"queue size: "<<Q.size()<<endl;
// 從隊(duì)列中移除元素,并輸出
while (!Q.empty()) {
int value = Q.front();
Q.pop();
cout << "Dequeued:" << value << endl;
}
return 0;
}
? 打印: ·
2. Qt QQueue 隊(duì)列基本用法
QQueue 繼承與 QList
? 常用方法: ·
QQueue<int> QQ; //定義一個(gè)int型隊(duì)列
QQ.isEmpty(); //返回隊(duì)列是否為空
QQ.size(); //返回隊(duì)列元素個(gè)數(shù)
QQ.clear(); //清空隊(duì)列
QQ.enqueue(); //在隊(duì)列尾部添加一個(gè)元素, 比如插入數(shù)字5: QQ.enqueue(5)
/* 相當(dāng)于
Q.push();
*/
QQ.dequeue(); //刪除當(dāng)前隊(duì)列第一個(gè)元素,并返回這個(gè)元素
/* 相當(dāng)于
Q.front(); //返回當(dāng)前隊(duì)列的第一個(gè)元素
Q.pop(); //從當(dāng)前隊(duì)列里,移出第一個(gè)元素
*/
QQ.head(); //返回當(dāng)前隊(duì)列第一個(gè)元素
/* 相當(dāng)于
Q.front();
*/
QQ.last(); //返回當(dāng)前隊(duì)列尾部的元素
/* 相當(dāng)于
Q.back();
*/
T & operator[]( int i ); //以數(shù)組形式訪問(wèn)隊(duì)列元素
? 實(shí)例: ·
#include <QCoreApplication>
#include <QQueue>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 創(chuàng)建一個(gè)QQueue對(duì)象
QQueue<int> QQ;
// 向隊(duì)列中添加元素
QQ.enqueue(1);
QQ.enqueue(2);
QQ.enqueue(3);
qDebug()<<"queue empty: "<<QQ.isEmpty();
qDebug()<<"queue size: " <<QQ.size();
qDebug()<<"queue head: " <<QQ.head() ;
qDebug()<<"queue last: " <<QQ.last() << "\n";
// 從隊(duì)列中移除元素,并輸出
while (!QQ.isEmpty()) {
int value = QQ.dequeue();
qDebug() << "Dequeued:" << value;
}
return a.exec();
}
? 打印: ·
3. Qt QQueue 多線程隊(duì)列
在多線程編程中,由于QQueue并不是線程安全的,因此我們需要先使用互斥鎖(QMutex)來(lái)保護(hù)隊(duì)列。在讀寫(xiě)隊(duì)列時(shí),我們需要獲取互斥鎖的鎖定,以避免多個(gè)線程同時(shí)訪問(wèn)隊(duì)列導(dǎo)致的數(shù)據(jù)競(jìng)爭(zhēng)問(wèn)題。
然后通過(guò)經(jīng)典的生產(chǎn)者和消費(fèi)者來(lái)看一個(gè)簡(jiǎn)單的示例程序,演示如何使用QQueue實(shí)現(xiàn)線程安全隊(duì)列:
#include <QCoreApplication>
#include <QQueue>
#include <QMutex>
#include <QThread>
#include <QDebug>
// 定義線程安全隊(duì)列類(lèi)
template<typename T>
class ThreadSafeQueue
{
public:
// 添加元素到隊(duì)列尾部
void enqueue(const T& value) {
QMutexLocker locker(&m_mutex);
m_queue.enqueue(value);
}
// 從隊(duì)列頭部移除一個(gè)元素,并返回它
T dequeue() {
QMutexLocker locker(&m_mutex);
if (m_queue.isEmpty()) {
return T();
}
return m_queue.dequeue();
}
// 返回隊(duì)列是否為空
bool isEmpty() const {
QMutexLocker locker(&m_mutex);
return m_queue.isEmpty();
}
private:
QQueue<T> m_queue;
mutable QMutex m_mutex;
};
// 定義生產(chǎn)者線程類(lèi)
class ProducerThread : public QThread
{
public:
ProducerThread(ThreadSafeQueue<int>& queue)
: m_queue(queue)
{
}
protected:
void run() override {
for (int i = 1; i <= 10; i++) {
m_queue.enqueue(i);
qDebug() << "Enqueued:" << i;
msleep(500);
}
}
private:
ThreadSafeQueue<int>& m_queue;
};
// 定義消費(fèi)者線程類(lèi)
class ConsumerThread : public QThread
{
public:
ConsumerThread(ThreadSafeQueue<int>& queue)
: m_queue(queue)
{
}
protected:
void run() override {
while (!isInterruptionRequested()) {
if (!m_queue.isEmpty()) {
int value = m_queue.dequeue();
qDebug() << "Dequeued:" << value;
}
msleep(500);
}
}
private:
ThreadSafeQueue<int>& m_queue;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 創(chuàng)建線程安全隊(duì)列對(duì)象
ThreadSafeQueue<int> queue;
// 創(chuàng)建生產(chǎn)者線程對(duì)象和消費(fèi)者線程對(duì)象
ProducerThread producer(queue);
ConsumerThread consumer(queue);
// 啟動(dòng)線程
producer.start();
consumer.start();
qDebug() << "F1";
// 等待線程結(jié)束
//在producer等待期間, consumer run()也會(huì)運(yùn)行 直到producer 運(yùn)行完畢,main才能往下執(zhí)行
producer.wait();
qDebug() << "F2";
consumer.requestInterruption(); //相當(dāng)于 ctrl + c 結(jié)束 consumer 線程
qDebug() << "F3";
consumer.wait();
qDebug() << "F4";
return a.exec();
}
? 運(yùn)行結(jié)果:
在上面的示例程序中,我們首先定義了一個(gè)模板類(lèi)ThreadSafeQueue,用于實(shí)現(xiàn)線程安全隊(duì)列。該類(lèi)使用QMutex來(lái)保護(hù)QQueue對(duì)象,以實(shí)現(xiàn)線程安全。
接下來(lái),我們定義了兩個(gè)線程類(lèi)ProducerThread和ConsumerThread,用于生產(chǎn)和
消費(fèi)數(shù)據(jù)。
在ProducerThread中,我們循環(huán)向隊(duì)列中添加元素,每隔500毫秒添加一個(gè)元素。在ConsumerThread中,我們循環(huán)從隊(duì)列中取出元素,每隔500毫秒取出一個(gè)元素。在取出元素時(shí),我們需要判斷隊(duì)列是否為空,避免出現(xiàn)異常情況。
其中:執(zhí)行 producer.wait() 時(shí), mian 中主線程將會(huì)被阻塞; 等到 producer 生產(chǎn)者運(yùn)行完畢,才會(huì)喚醒;而 consumer 線程不受影響;
萬(wàn)一發(fā)生數(shù)據(jù)處理速度不匹配的情況呢?
- 生產(chǎn)者休眠 500ms 消費(fèi)者休眠500ms, 就是如上情況
- 生產(chǎn)者休眠時(shí)間 < 消費(fèi)者休眠時(shí)間, 那么生產(chǎn)者執(zhí)行完畢后,消費(fèi)者還未消費(fèi)完就退出線程了,那么生產(chǎn)者必須暫停等待一下(阻塞生產(chǎn)者線程),以便等待消費(fèi)者線程把累積的數(shù)據(jù)處理完畢
- 生產(chǎn)者休眠時(shí)間 > 消費(fèi)者休眠時(shí)間, 那么生產(chǎn)者執(zhí)行完畢后,消費(fèi)者也執(zhí)行完畢了
真實(shí)的大數(shù)據(jù)情況下,如果生產(chǎn)者產(chǎn)出數(shù)據(jù)的速度大于消費(fèi)者消費(fèi)的速度,并且當(dāng)生產(chǎn)出來(lái)的數(shù)據(jù)累積到一定程度的時(shí)候,那么生產(chǎn)者必須暫停等待一下(阻塞生產(chǎn)者線程),以便等待消費(fèi)者線程把累積的數(shù)據(jù)處理完畢,反之亦然。在多線程環(huán)境下,我們每個(gè)程序員都必須去自己控制這些細(xì)節(jié),尤其還要兼顧效率和線程安全,而這會(huì)給我們的程序帶來(lái)不小的復(fù)雜度。
在 java 中有 BlockingQueue 阻塞隊(duì)列,但是Qt 中似乎沒(méi)有相關(guān)的阻塞隊(duì)列,需要我們自己去自己控制這些細(xì)節(jié)
4. Qt BlockingQueue 自定義線程安全的阻塞隊(duì)列
以下定義一個(gè)簡(jiǎn)單的阻塞隊(duì)列:
#include <QCoreApplication>
#include <QWaitCondition>
#include <QQueue>
#include <QMutex>
#include <QThread>
#include <QDebug>
template <typename T>
class BlockingQueue
{
public:
BlockingQueue() {}
void put(const T& value)
{
QMutexLocker locker(&m_mutex);
m_queue.enqueue(value);
m_condition.wakeOne(); //喚醒等待隊(duì)列中的一個(gè)線程(來(lái)自wait)
}
T take()
{
QMutexLocker locker(&m_mutex);
while (m_queue.isEmpty()) {
m_condition.wait(&m_mutex);
}
return m_queue.dequeue();
}
bool isEmpty() const
{
QMutexLocker locker(&m_mutex);
return m_queue.isEmpty();
}
int size() const
{
QMutexLocker locker(&m_mutex);
return m_queue.size();
}
private:
QQueue<T> m_queue;
mutable QMutex m_mutex;
QWaitCondition m_condition;
};
這個(gè) BlockingQueue類(lèi)使用QMutex和QWaitCondition來(lái)保證線程安全,并實(shí)現(xiàn)了put、take、isEmpty和size等方法。其中,put方法用于往隊(duì)列中插入元素,take方法用于從隊(duì)列中取出元素,isEmpty方法用于判斷隊(duì)列是否為空,size方法用于獲取隊(duì)列中元素的數(shù)量。
在put方法中,我們首先獲取了互斥鎖,然后將元素插入到隊(duì)列中,并通過(guò)QWaitCondition的wakeOne()方法喚醒一個(gè)等待線程(調(diào)用take()中的線程)。在take方法中,我們首先獲取了互斥鎖,然后在隊(duì)列為空時(shí)調(diào)用QWaitCondition的wait()方法等待,直到有其他線程往隊(duì)列中插入了元素并喚醒了當(dāng)前線程。
mutable的作用是允許在const成員函數(shù)中修改BlockingQueue類(lèi)的m_mutex和m_notEmpty成員變量。這是因?yàn)?,生產(chǎn)者和消費(fèi)者線程在往阻塞隊(duì)列中添加或刪除元素時(shí),都需要對(duì)這兩個(gè)成員變量進(jìn)行修改。但是,由于take()和tryTake()方法都是const成員函數(shù),因此如果不將m_mutex和m_notEmpty聲明為mutable類(lèi)型,編譯器就會(huì)報(bào)錯(cuò)。
? 使用: ·
static BlockingQueue<int> queue;
class Producer : public QThread
{
public:
void run() override
{
for (int i = 0; i < 10; ++i) {
queue.put(i);
qDebug() << "Producer thread: " << QThread::currentThreadId() << ", value: " << i;
msleep(500); // sleep for 0.5 second
}
}
};
class Consumer : public QThread
{
public:
void run() override
{
int value = 0;
while (true) {
value = queue.take();
qDebug() << "Consumer thread: " << QThread::currentThreadId() << ", value: " << value;
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer producer;
Consumer consumer1, consumer2;
producer.start();
consumer1.start();
consumer2.start();
return a.exec();
}
? 運(yùn)行結(jié)果:
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-563846.html
上述包含一個(gè)生產(chǎn)者線程和兩個(gè)消費(fèi)者線程,生產(chǎn)者線程往隊(duì)列中插入10個(gè)整數(shù),每插入一個(gè)元素后暫停0.5秒。兩個(gè)消費(fèi)者線程不斷從隊(duì)列中取出元素,并輸出當(dāng)前線程的ID和取出的元素值。 當(dāng)隊(duì)列為空時(shí),消費(fèi)者線程會(huì)進(jìn)入等待狀態(tài),直到有其他線程往隊(duì)列中插入元素。 文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-563846.html
到了這里,關(guān)于Qt QQueue 安全的多線程隊(duì)列、阻塞隊(duì)列的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!