解决Qt多线程中fromRawData函数生成的QByteArray数据不一致问题
 
目录
- 🔔 问题背景
 - 📄 问题代码
 - ❓ 问题描述
 - 🩺 问题分析
 - ✔ 解决方案
 
🔔 问题背景
在开发一个使用Qt框架的多线程应用程序时,我们遇到了一个棘手的问题:在不同线程中打印同一个
QByteArray对象的内容时,得到了不一致的结果。这个问题出现在一个设备通信类中,该类使用一个工作线程来读取设备数据,然后通过信号-槽机制将数据传递到主线程进行处理。
📄 问题代码
代码结构如下:
class DeviceManager : public QObject {
public:
    DeviceManager() : QObject() {
        m_worker = new Worker();
        m_workerThread = new QThread(this);
        m_worker->moveToThread(m_workerThread);
        connect(m_workerThread, &QThread::started, m_worker, &Worker::readDevice);
        connect(m_worker, &Worker::dataReady, this, &DeviceManager::processData);
        m_workerThread->start();
    }
    ~DeviceManager() {
    	m_hidWork->breakFlag = false;
    	m_hidWorkThread->quit();
    	m_hidWorkThread->wait();
    	m_hidWorkThread->deleteLater();
    }
private:
    void processData(QByteArray data) {
        qDebug() << "Main thread: " << data.toHex(' ');
    }
    class Worker;
    Worker *m_worker;
    QThread *m_workerThread;
};
class DeviceManager::Worker : public QObject {
    Q_OBJECT
public:
    Worker(QObject *parent = nullptr) : QObject(parent) {}
	
	bool breakFlag = true;
	
    void readDevice() {
        auto buffer = new char[1024];
        while(breakFlag) {
        	size_t size = 5;
        	memset(buffer, 0, size)
        	// 这里是模拟读取设备数据
        	buffer = {0x01, 0x02, 0x03, 0x04, 0x05};
        	QByteArray deviceData = QByteArray::fromRawData(reinterpret_cast<char*>(buffer), size);
        	qDebug() << "Worker thread: " << deviceData.toHex(' ');
        	emit dataReady(QByteArray(deviceData));
        }
    }
signals:
    void dataReady(QByteArray data);
};
 
❓ 问题描述
在运行这段代码时,当主线程进行了一个UI阻塞操作的时候,我们观察会到工作线程和主线程中打印的QByteArray内容不一致。例如:
Worker thread: 01 02 03 04 05
Main thread: 00 00 00 00 00
 
这显然不是我们期望的结果,因为两个线程应该打印相同的数据。
🩺 问题分析
经过仔细分析,我们发现问题的根源在于QByteArray::fromRawData()的使用方式。这个函数创建了一个共享底层数据的QByteArray,而不是复制数据。这意味着:
fromRawData()创建的QByteArray与原始缓冲区共享数据。- 当原始缓冲区被修改或释放时,
QByteArray的内容可能变得无效。 - 在跨线程传递时,如果原始数据发生变化,接收线程可能会得到意外的结果。
 
✔ 解决方案
解决这个问题的关键是确保在发送信号时创建QByteArray的完整副本。以下是修改后的Worker::readDevice()方法:
void Worker::readDevice() {
	auto buffer = new char[1024];
    while(breakFlag) {
       	size_t size = 5;
       	memset(buffer, 0, size)
       	// 这里是模拟读取设备数据
       	buffer = {0x01, 0x02, 0x03, 0x04, 0x05};
       	//QByteArray deviceData = QByteArray::fromRawData(reinterpret_cast<char*>(buffer), size);
	    QByteArray deviceData((reinterpret_cast<char*>(buffer), size);  // 创建数据的副本
	    qDebug() << "Worker thread: " << deviceData.toHex(' ');
	    emit dataReady(deviceData);
	}	    
}
 
这里的改动看似很小,但却解决了问题:
- 我们使用
QByteArray(const char *data, int size)构造函数来创建QByteArray。 - 这个构造函数会复制提供的数据,而不是共享它。
 - 结果是一个独立的
QByteArray对象,其内容不会受到原始缓冲区变化的影响。 



















