Qt C++单类运行正常,基于QThread重构列表转字符串功能时程序崩溃求助
我来帮你拆解下程序崩溃的核心原因,一步步给出修复方案:
1. 野指针访问(最直接的崩溃诱因)
先看你的add_Item_to_List函数:
void MainWindow::add_Item_to_List(ITEM newI) { Item_List.append(newI); TString->setList(Item_List); TString->start(); }
你的Item_List是QList<ITEM*>类型,但你直接把**栈上的临时对象newI**塞进了指针列表。当add_Item_to_List执行完毕,栈上的newI会被自动销毁,此时Item_List里的指针就变成了野指针。线程在run()里调用I->getString()时,访问的是已经被释放的内存,必然触发崩溃。
修复方案
需要在堆上创建ITEM对象,再将堆指针存入列表:
void MainWindow::add_Item_to_List(ITEM newI) { // 在堆上创建对象,拷贝传入的newI数据 ITEM* heapItem = new ITEM(newI); Item_List.append(heapItem); TString->setList(Item_List); TString->start(); }
⚠️ 注意:后续要在合适时机(比如窗口关闭时)遍历Item_List,手动delete每个指针,避免内存泄漏。
2. QThread重复启动的非法操作
每次调用add_Item_to_List都会执行TString->start(),但QThread不允许重复启动。如果前一次线程还在运行,再次调用start()会触发Qt的断言失败,直接导致崩溃。
另外,你线程run()函数里的terminate()和wait()完全多余:QThread的run()执行完毕后,线程会自动进入结束状态,terminate()是强制终止(非常不安全),wait()等待自己结束更是逻辑错误。
修复方案
- 先移除
run()里的terminate();和wait();代码; - 启动线程前检查状态,或者每次创建新线程:
方案A:等待前线程结束再启动
void MainWindow::add_Item_to_List(ITEM newI) { ITEM* heapItem = new ITEM(newI); Item_List.append(heapItem); // 线程正在运行的话,先等待它结束 if (TString->isRunning()) { TString->wait(); } TString->setList(Item_List); TString->start(); }
方案B:每次创建新线程(更灵活)
void MainWindow::add_Item_to_List(ITEM newI) { ITEM* heapItem = new ITEM(newI); Item_List.append(heapItem); String_Writer_Thread* newThread = new String_Writer_Thread(this); connect(newThread, &String_Writer_Thread::EMIT_STRING, this, &MainWindow::update_Output); // 线程结束后自动销毁对象,避免内存泄漏 connect(newThread, &QThread::finished, newThread, &QObject::deleteLater); newThread->setList(Item_List); newThread->start(); }
3. 多线程并发访问的线程安全问题
你的setList函数直接修改ITEM_LIST,而线程的run()函数正在遍历这个列表。如果主线程调用setList时,子线程刚好在遍历,会出现并发访问同一容器的情况,导致容器内部结构被破坏,引发崩溃。
修复方案
给ITEM_LIST添加互斥锁,确保读写操作互斥:
- 在
String_Writer_Thread类中添加互斥锁成员:
// string_writer_thread.h #include <QMutex> class String_Writer_Thread : public QThread { Q_OBJECT // ... 其他成员 private: QList<ITEM*> ITEM_LIST; QMutex m_mutex; // 新增互斥锁 };
- 修改
setList和run函数,用QMutexLocker自动加解锁:
// 设置列表时加锁 void String_Writer_Thread::setList(QList<ITEM*> IList) { QMutexLocker locker(&m_mutex); ITEM_LIST.clear(); foreach(auto I, IList) { ITEM_LIST.append(I); } } // 遍历列表时加锁 void String_Writer_Thread::run() { QString XString; QMutexLocker locker(&m_mutex); foreach(auto *I, ITEM_LIST) { XString.append(I->getString()); } emit EMIT_STRING(XString); }
QMutexLocker会在作用域结束时自动解锁,比手动调用lock()/unlock()更安全,避免忘记解锁导致死锁。
4. 额外注意:ITEM类的拷贝构造函数
如果你用了new ITEM(newI),要确保ITEM类有正确的拷贝构造函数,否则拷贝出的对象数据会异常。如果ITEM包含指针成员,还需要实现深拷贝:
// item.h class ITEM { public: ITEM() = default; // 拷贝构造函数 ITEM(const ITEM& other) { // 手动拷贝所有成员示例 this->name = other.name; this->size = other.size; // 若size是结构体,确保能正确拷贝 this->color = other.color; } // ... 其他成员函数 };
把以上问题全部修复后,程序应该就能稳定运行了。
内容的提问来源于stack exchange,提问作者Martin Sieburg

