You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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添加互斥锁,确保读写操作互斥:

  1. 在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; // 新增互斥锁
};
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 18:42:51