工作过程中用到了Qt的多线程,本来实现了一个发送接收数据的线程,为了排查一些问题而复习了一下Qt锁的一些知识。
假设用一个管理类Manager,里边管理了一个数据接收线程,同时封装了一个saveData的接口:
#ifndef TMPTHREAD_H
#define TMPTHREAD_H
#include <QDebug>
#include <QThread>
#include <QMutex>
#include <QQueue>
#include <QMutexLocker>
class TMPThread: public QThread
{
public:
TMPThread();
void saveData(int i);
void printData();
protected:
void run() override;
private:
int _data;
QMutex _mutex;
QQueue<int> _data_queue;
};
#endif // TMPTHREAD_H
其中,_data是一个临时记录的数据,_mutex是锁,_data_queue是数据接收队列,具体实现如下:
#include "TMPThread.h"
TMPThread::TMPThread()
: _mutex(QMutex::NonRecursive) // <----------- 1
{
}
void TMPThread::saveData(int i)
{
QMutexLocker lock(&_mutex);
_data_queue.enqueue(i);
if(!this->isRunning() && !_data_queue.isEmpty()){
this->start();
// this->printData(); // <--------------- 2
}
}
void TMPThread::printData()
{
while(!_data_queue.isEmpty()){
QMutexLocker lock(&_mutex);
QThread::sleep(1);
auto data = _data_queue.dequeue();
qDebug() << "Curr data: " << QString::number(data);
qDebug() << "Curr queue length: " << QString::number(_data_queue.size());
lock.unlock();
}
}
void TMPThread::run()
{
while(!_data_queue.isEmpty()){
QMutexLocker lock(&_mutex);
QThread::sleep(1);
auto data = _data_queue.dequeue();
qDebug() << "Curr data: " << QString::number(data);
qDebug() << "Curr queue length: " << QString::number(_data_queue.size());
lock.unlock();
}
}
在1处,可以选择声明锁的类型,Qt默认是递归锁,这里选择手动声明为非递归锁,为了验证问题。重新写了run函数后,实验时发现如果选择this->start()启动,不论锁的类型是什么,都不会死锁,同时也说明了start并非按照自己理解的那样工作,但是如果重新写了printData函数,相当于递归调用了,那么会造成死锁了。
数据管理类实现:
#ifndef MANAGER_H
#define MANAGER_H
#include <QDebug>
#include <QScopedPointer>
#include "TMPThread.h"
class Manager
{
public:
Manager();
~Manager();
void startAllThreads();
private:
QScopedPointer<TMPThread> _thread;
QScopedPointer<QByteArray> _buffer1;
QScopedPointer<QByteArray> _buffer2;
};
#endif // MANAGER_H
这里在模拟一个双缓冲,后续重新考虑实现,现有的具体实现如下:
#include "Manager.h"
Manager::Manager()
: _thread(new TMPThread)
, _buffer1(new QByteArray)
, _buffer2(new QByteArray)
{
}
Manager::~Manager()
{
qDebug() << "Delete Manager!";
}
void Manager::startAllThreads()
{
qDebug() << "Manger: Start Save Data!";
for(int i = 0; i < 10; i++){
_thread->saveData(i);
}
}
在主函数引用Manager,并且调用实验:
#include <QCoreApplication>
#include <QThread>
#include <QScopedPointer>
#include "Manager.h"
int main(int argc, char *argv[])
{
QScopedPointer<Manager> manager{new Manager};
manager->startAllThreads();
QThread::sleep(20);
qDebug() << "Finished!";
return 0;
}
以上就是问题的一些发现。