网站安全满分实战:从HTTPS到CSP的全面加固指南
2026/9/15 23:05:43
生产者-消费者模式是一种经典的多线程设计模式,核心作用是解耦数据的产生和处理过程,让两者可以独立运行、协同工作。
就像餐厅里:
项目采用"生产者-消费者+UI控制"的三层结构,核心组件包括:
QMutex保证队列操作(存/取)的互斥性,避免同时读写导致数据混乱。QWaitCondition实现"队空时消费者等待,有数据时唤醒"的逻辑,减少无效轮询。pushBatch/popBatch),提高效率。DataQueue的push接口存入缓冲区。QThread,重写run方法实现数据生成循环。m_isRunning标记控制循环启停,通过startProduce/stopProduce接口外部控制。generateTestData(全局函数),与生产者解耦。moveToThread实现),避免阻塞UI。QTimer定时(2ms)轮询缓冲区(onPollQueue),批量取数据(popBatch)。setNewFileName),切换时自动创建新文件并写入表头。m_fileMutex),保证线程安全。ProducerThread::startProduce)。QMetaObject::invokeMethod+Qt::QueuedConnection)。| 模块 | 接口名 | 功能描述 |
|---|---|---|
| DataQueue | push(item, tag) | 单个数据存入队列(线程安全) |
| DataQueue | popBatch(out, size) | 批量从队列取数据(队空时阻塞等待) |
| ProducerThread | startProduce() | 启动生产者线程,开始生成数据 |
| ProducerThread | stopProduce() | 停止生产者线程,优雅退出循环 |
| CsvFileSaver | start() | 启动消费者线程,开始轮询队列 |
| CsvFileSaver | setNewFileName(name) | 标记切换新文件(下次取数据时生效) |
| CsvFileSaver | startSaving() | 开启数据保存(仅控制标记,不影响线程) |
| MainWindow | onConfirmFileName() | 处理UI输入,触发文件名切换 |
QMutex对队列的所有读写操作加锁(DataQueue中所有方法均通过QMutexLocker加锁)。m_isRunning、m_isSaving)通过互斥锁保护,避免读写冲突。QWaitCondition阻塞等待(DataQueue::pop中的wait),有数据时被唤醒,减少CPU占用。m_maxCapacity),满时自动删除老数据(push中takeFirst),控制内存使用。QMetaObject::invokeMethod+Qt::QueuedConnection实现跨线程安全调用(如MainWindow::onConfirmFileNameClicked中设置文件名)。moveToThread移到子线程,避免"对象在主线程,方法在子线程执行"的混乱。CsvFileSaver::stop),先停定时器、关闭文件,再退出线程。m_isRunning标记控制循环退出,避免terminate(强制终止线程)的危险操作。QThread::started信号)再启动定时器,并绑定Qt::DirectConnection(CsvFileSaver构造函数中),确保定时器在子线程的事件循环中运行。moveToThread移到子线程(非重写run),用信号槽驱动逻辑(如CsvFileSaver)。run方法实现循环(如ProducerThread),但需注意run中无事件循环,定时器等需手动处理。start()(触发run或事件循环)。quit()(退出事件循环)+wait()(等待线程结束),避免terminate()(强制终止可能导致资源泄露)。QMutex)保护。timeout信号与槽函数的连接方式需注意:Qt::AutoConnection(默认)。Qt::DirectConnection(效率更高)。QMutex或QReadWriteLock保护,避免竞态条件。QMetaObject::invokeMethod+Qt::QueuedConnection(适用于需要立即调用的场景)。mutable的实际用途在 C++ 中,mutable是一个关键字,其核心作用是允许在const成员函数中修改被其修饰的成员变量。这打破了 “const成员函数不能修改对象成员” 的默认规则,主要用于那些 “逻辑上不属于对象状态,但需要被修改” 的成员变量。
在你的项目中,mutable主要用于修饰互斥锁(如QMutex),例如:
// csvfilesaver.h 中mutableQMutex m_headerMutex;// 表头操作锁(线程安全)mutableQMutex m_runMutex;// 运行标记锁mutableQMutex m_saveMutex;// 保存控制锁mutableQMutex m_fileNameMutex;// 文件名变更锁mutable?互斥锁(QMutex)的作用是保证多线程对共享资源的安全访问,其核心操作是lock()和unlock()—— 这两个操作会修改互斥锁自身的状态(比如从 “未锁定” 变为 “锁定”)。
而项目中访问这些锁的函数可能是const成员函数(例如获取状态的函数)。例如:
// 获取表头字符串(逻辑上是“读取”操作,声明为 const 更合理)QStringCsvFileSaver::getHeaderString()const{QMutexLockerlocker(&m_headerMutex);// 这里会调用 m_headerMutex.lock(),修改锁的状态returnm_headerList.join(",")+"\n";}getHeaderString()是 “读取” 操作,逻辑上不需要修改对象的核心状态(如m_headerList的内容),因此声明为const是合理的。m_headerMutex以保证线程安全,而lock()操作会修改m_headerMutex的状态。如果m_headerMutex没有被mutable修饰,编译器会报错(const函数中不能修改非mutable成员)。而mutable允许这种修改,因为互斥锁的状态变化属于 “实现细节”,不属于对象的 “逻辑状态”(用户关心的是m_headerList的值,而不是锁的状态)。
mutable的核心场景const函数:当const成员函数需要通过互斥锁(QMutex等)保证线程安全时,锁对象必须用mutable修饰,否则无法在const函数中执行lock()/unlock()。const函数中更新,此时可用mutable。项目地址https://gitee.com/sun874573943/my-gitee-pro.git