Qt多线程Qrunnable问题,connect问题

把一个结构体指针传入一个类,构造函数中指针的值是对的,到成员函数中,debug模式跟踪的值就变成0xcd,cd、cd、cdcd、cd0000了,但在成员函数中依然能按构造函数的值访问结构体里的成员,但是用emit发射信号时,将结构体里各个值发射出去的时候,值会改变,乱码崩溃都有可能,每个emit之前都把结构体中的成员打印一遍,都是正常的。

class QThreadPool;
class QTableWidgetItem;
class QMenu;
class QAction;
class RunExe : public QObject, public QRunnable
{
    Q_OBJECT
public:
    typedef struct /*exeStruct*/{
        long long mUid = -1;   //uid
        QString mName;  //名称
        QString mPath;
        int mPriority = 0;  //优先级
        QString mStatus = "Waiting";
        QString mExePath;       //程序路径
        QString mExeParamers;   //程序参数

    }EXE;
    RunExe(EXE *, QTableWidgetItem *);
    ~RunExe();
    
    bool isRunning() const;
    void waitForFinished();
    long long getUid() const;
    void setCancel();
    
protected:
    void run();

    
signals:
    void sigExeFinished(const long long &, const QString &, const QString &, QTableWidgetItem *);
    
private:
    EXE *mParamer;
    QTableWidgetItem *pItem;
    bool isCancel;
    bool isFinished;
};

class TaskMonitor : public QWidget
{
    Q_OBJECT
    
public:
    explicit TaskMonitor(QWidget *parent = nullptr);
    ~TaskMonitor();
    
    void setProjPath(const QString &);
    void setExeInfo(const RunExe::EXE &);
    void closeSoft();   //程序关闭
    
private:
    void iniRightMenu();
    void iniInterface();
    
    void saveTask();
    void loadTask();
    
protected:
    void contextMenuEvent(QContextMenuEvent *); //显示右键菜单
    
signals:
    void sigUpdateResult(const QString &);
    void sigResultError(const QString &);
    
private slots:
    void sltAlterTaskPriority(); //修改任务优先级
    void sltExeFinished(const long long &, const QString &, const QString &, QTableWidgetItem *);
    
private:
    Ui::TaskMonitor *ui;
    static QMutex mutex;
    QString mProjectPath;   //工程路径
    QThreadPool *pCallExePool;  //线程池
    QMap<long long, RunExe *> vecRunExes;
    QVector<RunExe::EXE> vecExes;
    
    QMenu *pRightMenu;
    QAction *pAlterTaskPriority;   //修改像元尺寸
};

#endif // CALLEXE_H

上面是头文件
在主线程里调用

RunExe::EXE exe;
                        exe.mUid = mProjectInfo.mCalibrationModes[n]->mUid;
                        exe.mName = file.baseName();MLOG<<"file.baseName() = "<<file.baseName();
                        exe.mPath = mProjectInfo.mCalibrationModes[n]->mSavePath + "/" + prjFile.baseName() + CALIBRATIONSUFFIXXML;
                        exe.mExePath = QCoreApplication::applicationDirPath() + "/test/xx.exe";

                        
                        exe.mExeParamers.push_back(calibrationPath);
                        Optima::CheckerBoard *caliMode = dynamic_cast<Optima::CheckerBoard *>(mProjectInfo.mCalibrationModes[0]);
                        exe.mExeParamers.push_back("|"+QString::number(caliMode->nCheckerBoardNumOfX));
                        exe.mExeParamers.push_back("|"+QString::number(caliMode->nCheckerBoardNumOfY));
                        exe.mExeParamers.push_back("|"+QString::number(caliMode->fCheckerBoardSize));
                        exe.mExeParamers.push_back("|"+QString::number(mProjectInfo.mCamInfos[0].mUid)+QString::number(mProjectInfo.mCamInfos[0].calibNum+1)+".txt");
                        
                        pTaskMonitor->setExeInfo(exe);

进入setExeInfo函数

void TaskMonitor::setExeInfo(const RunExe::EXE &exe)
{
    QList<QTableWidgetItem *> items = ui->CallExe_Table->findItems(QString::number(exe.mUid), Qt::MatchExactly | Qt::MatchRecursive);
    if (!items.isEmpty())
    {
        if (vecRunExes.find(exe.mUid) != vecRunExes.end())
            return;

        int row = items[0]->row();
        if (ui->CallExe_Table->item(row, 3)->text() != "Completed" || ui->CallExe_Table->item(row, 3)->text() != "Error")
            return;

        if (!pCallExePool->tryTake(vecRunExes[exe.mUid]))
            return;

        vecRunExes.remove(exe.mUid);

        ui->CallExe_Table->item(row, 3)->setText("Waiting");
        for (int n = 0; n < vecExes.size(); ++n)
        {
            if (vecExes[n].mUid != exe.mUid)
                continue;

            RunExe *_run = new RunExe(&vecExes[n], ui->CallExe_Table->item(row, 3));
            connect(_run, &RunExe::sigExeFinished, this, &TaskMonitor::sltExeFinished, Qt::BlockingQueuedConnection);MLOG;
            //connect(_run, &RunExe::sigExeFinished, this, &TaskMonitor::sltExeFinished, Qt::QueuedConnection);
            _run->setAutoDelete(true);
            vecRunExes[exe.mUid] = _run;
            pCallExePool->start(_run);

            break;
        }
        saveTask();
        return;
    }
MLOG;
    int row = ui->CallExe_Table->rowCount();
    ui->CallExe_Table->setRowCount(row + 1);

    //id
    ui->CallExe_Table->setItem(row, 0, new QTableWidgetItem(QString::number(exe.mUid)));
    ui->CallExe_Table->item(row, 0)->setToolTip(QString::number(exe.mUid));
    ui->CallExe_Table->item(row, 0)->setTextAlignment(Qt::AlignCenter);
    ui->CallExe_Table->item(row, 0)->setFlags(ui->CallExe_Table->item(row, 0)->flags() ^ Qt::ItemIsEditable);

    //name
    ui->CallExe_Table->setItem(row, 1, new QTableWidgetItem(exe.mName));
    ui->CallExe_Table->item(row, 1)->setToolTip(exe.mName);
    ui->CallExe_Table->item(row, 1)->setTextAlignment(Qt::AlignCenter);
    ui->CallExe_Table->item(row, 1)->setFlags(ui->CallExe_Table->item(row, 1)->flags() ^ Qt::ItemIsEditable);

    //Priority
    ui->CallExe_Table->setItem(row, 2, new QTableWidgetItem(QString::number(exe.mPriority)));
    ui->CallExe_Table->item(row, 2)->setToolTip(QString::number(exe.mPriority));
    ui->CallExe_Table->item(row, 2)->setTextAlignment(Qt::AlignCenter);
    ui->CallExe_Table->item(row, 2)->setFlags(ui->CallExe_Table->item(row, 2)->flags() ^ Qt::ItemIsEditable);

    //status
    ui->CallExe_Table->setItem(row, 3, new QTableWidgetItem(exe.mStatus));
    ui->CallExe_Table->item(row, 3)->setToolTip("Waiting");
    ui->CallExe_Table->item(row, 3)->setTextAlignment(Qt::AlignCenter);
    ui->CallExe_Table->item(row, 3)->setFlags(ui->CallExe_Table->item(row, 3)->flags() ^ Qt::ItemIsEditable);

    vecExes.push_back(exe);
    if (exe.mStatus == "Waiting")
    {
        RunExe *_run = new RunExe(&vecExes[vecExes.size() - 1], ui->CallExe_Table->item(row, 3));
        connect(_run, &RunExe::sigExeFinished, this, &TaskMonitor::sltExeFinished, Qt::BlockingQueuedConnection);
        //connect(_run, &RunExe::sigExeFinished, this, &TaskMonitor::sltExeFinished, Qt::QueuedConnection);
        _run->setAutoDelete(true);
        vecRunExes[exe.mUid] = _run;
        pCallExePool->start(_run);
    }
    saveTask();
}

然后就进入run函数

QProcess *pPro = new QProcess;
    MLOG<< mParamer->mExeParamers.split("|");
    QStringList paraList=mParamer->mExeParamers.split("|");
    MLOG<<paraList.size();
    QString uidCalibNum=paraList.at(4);
    pPro->start(mParamer->mExePath, mParamer->mExeParamers.split("|"));
    mParamer->mStatus = "Starting";

    MLOG<<"mparamer = "<<mParamer;
    MLOG<<"mParamer->mUid = "<<mParamer->mUid;
    MLOG<<"mParamer->mPath+,+uidCalibNum = "<<mParamer->mPath+","+uidCalibNum;
    MLOG<<"mParamer->mStatus = "<<mParamer->mStatus;
    MLOG<<"pItem = "<<pItem;
    emit sigExeFinished(mParamer->mUid, mParamer->mPath+","+uidCalibNum, mParamer->mStatus, pItem);
    if (pPro->waitForStarted(-1))
    {
        mParamer->mStatus = "Running";
        MLOG<<"mParamer->mUid = "<<mParamer->mUid;
        MLOG<<"mParamer->mPath+,+uidCalibNum = "<<mParamer->mPath+","+uidCalibNum;
        MLOG<<"mParamer->mStatus = "<<mParamer->mStatus;
        MLOG<<"pItem = "<<pItem;
        emit sigExeFinished(mParamer->mUid, mParamer->mPath+","+uidCalibNum, mParamer->mStatus, pItem);
        while (!pPro->waitForFinished(10))
        {
            if (isCancel) break;
//            if(isFinishedLiang) break;
//            qDebug()<<"not finish";
        }

        if (isCancel) {
            if (pPro) {
                pPro->kill();
                pPro->waitForFinished(-1);
                delete pPro;
                pPro = nullptr;
            }
            isFinished = true;
            mParamer->mStatus = "Error";
            MLOG<<isCancel;
            MLOG<<"mParamer->mUid = "<<mParamer->mUid;
            MLOG<<"mParamer->mPath+,+uidCalibNum = "<<mParamer->mPath+","+uidCalibNum;
            MLOG<<"mParamer->mStatus = "<<mParamer->mStatus;
            MLOG<<"pItem = "<<pItem;
            emit sigExeFinished(mParamer->mUid, mParamer->mPath+","+uidCalibNum, mParamer->mStatus, pItem);
            return;
        }

        int exitCode = pPro->exitCode();
        qDebug()<<pPro->exitCode()<<" = pPro->exitCode()";
        if (exitCode == 0)
        {
            delete pPro;MLOG;
            pPro = nullptr;MLOG;
            isFinished = true;MLOG;
            //mParamer->mStatus = QString("Completed");qDebug()<<__FUNCTION__<<__LINE__;
            MLOG<<"mParamer->mUid = "<<mParamer->mUid;
            MLOG<<"mParamer->mPath++uidCalibNum = "<<mParamer->mPath+","+uidCalibNum;
            MLOG<<"mParamer->mStatus = "<<mParamer->mStatus;
            MLOG<<"pItem = "<<pItem;
            emit sigExeFinished(mParamer->mUid, mParamer->mPath+","+uidCalibNum, /*mParamer->mStatus*/QString("Completed"), pItem);MLOG;
            return;
        }
    }
    delete pPro;MLOG;
    pPro = nullptr;
    isFinished = true;
    mParamer->mStatus = "Error";
    MLOG<<"mParamer->mUid = "<<mParamer->mUid;
    MLOG<<"mParamer->mPath+,+uidCalibNum = "<<mParamer->mPath+","+uidCalibNum;
    MLOG<<"mParamer->mStatus = "<<mParamer->mStatus;
    MLOG<<"pItem = "<<pItem;
    emit sigExeFinished(mParamer->mUid, mParamer->mPath+","+uidCalibNum, mParamer->mStatus, pItem);

run函数里打印mParamer的任何数据都没问题,但是emit出去,到sltExeFinished函数里,有时候到无法访问的地址直接崩溃,有时候uid不对,有时候status是乱码。下面是sltexefinished函数

void TaskMonitor::sltExeFinished(const long long &uid, const QString &path, const QString &status, QTableWidgetItem *item)
{
    qDebug()<<__FUNCTION__<<__LINE__<<uid<<status;
//    if (vecRunExes.find(uid) != vecRunExes.end())
//        vecRunExes.remove(uid);
//    saveTask();
//    //QStringList strList=path.split(",");
//    item->setText(status);
//    if (status == "Completed")
//        emit sigUpdateResult(path);
//    else if (status == "Error")
//        emit sigResultError(path);
}

下面是debug模式跟踪到构造函数和run函数的截图

img

img

  1. 看一下参数传递前后地址是否相同,看你的报错应该是不一样了貌似
  2. 改用值传递
    可参考链接: https://blog.csdn.net/u011555996/article/details/124436777

既然值是0xcdcd了,肯定是内存在哪里被释放掉了,访问被释放掉的内存出现值不对、乱码崩溃就都有可能,定位一下这个结构体有没有在哪被释放。没有完整代码,我也没法帮你定位。
如有帮助,请采纳,谢谢。

引用chatgpt内容作答:
我理解你想要对代码进行修改以解决问题。尽管我不能为你直接运行或测试代码,但我可以提供一些可能的修正方向。以下是一些可能需要修改的地方:

1、线程安全问题:
你在RunExe类中访问mParamer结构体的成员,而这个结构体被多个线程访问。你需要确保对这个结构体的访问是线程安全的。可以在访问mParamer时使用互斥锁,以确保在多线程环境下的数据安全。在RunExe类中添加一个互斥锁,然后在访问和修改mParamer成员时使用这个锁。

2、信号槽连接:
你在setExeInfo函数中使用了Qt::BlockingQueuedConnection连接类型,这会导致信号的槽函数在主线程中同步执行。如果你的槽函数执行时间较长,会阻塞主线程,影响程序的响应性。尝试使用默认的Qt::AutoConnection连接类型,这样信号的槽函数会根据情况自动在不同线程中执行。

3、内存管理问题:
确保在信号发射之后,信号槽连接中使用的所有指针仍然是有效的。如果你在信号槽连接之后删除了对象,那么连接的槽函数可能会访问无效的内存。你可以在信号槽连接之前,确保所有连接的指针在信号处理之前保持有效。

4、优化信号发射:
在run函数中的多次信号发射可能会导致性能问题。你可以尝试将这些发射信号的操作合并为一个操作,以减少信号发射的频率。

以下是示意性的代码片段,展示了可能的修改方向:

// 在RunExe类中添加互斥锁
class RunExe : public QObject, public QRunnable
{
    // ...
private:
    QMutex mMutex; // 添加互斥锁
};

// 修改run函数中对mParamer的访问
void RunExe::run()
{
    // 使用互斥锁保护mParamer的访问
    QMutexLocker locker(&mMutex);
    // 访问和修改mParamer成员
    // ...

    // 发射信号
    emit sigExeFinished(/*...*/);
}

// 修改setExeInfo函数中的信号连接
void TaskMonitor::setExeInfo(const RunExe::EXE &exe)
{
    // ...

    // 修改连接类型为Qt::AutoConnection
    connect(_run, &RunExe::sigExeFinished, this, &TaskMonitor::sltExeFinished, Qt::AutoConnection);

    // ...
}

// 在信号处理函数中确保对象的有效性
void TaskMonitor::sltExeFinished(const long long &uid, const QString &path, const QString &status, QTableWidgetItem *item)
{
    Q_UNUSED(uid);
    Q_UNUSED(path);
    Q_UNUSED(status);
    Q_UNUSED(item);

    // 确保相关对象在信号处理期间有效
    // ...

    // 执行信号处理操作
    // ...
}

这些修改是基于我对问题的理解,并可能需要进一步的调试和测试。同时,这些修改可能无法涵盖所有潜在问题,所以你可能需要根据实际情况进行调整。在做任何修改之前,强烈建议备份你的代码以防意外情况。

【以下回答由 GPT 生成】

对于这个问题,可能有几个原因导致结构体指针的值在调试时显示不正确,但在成员函数中仍然可以访问结构体的成员。

首先,确保你在构造函数中正确地初始化结构体指针。你可以在构造函数中添加一些调试输出语句来确认指针是否正确赋值。

RunExe::RunExe(EXE *paramer, QTableWidgetItem *item) : mParamer(paramer), pItem(item)
{
    qDebug() << "Pointer value in constructor: " << mParamer;
    // ...
}

确保在调用构造函数时传递正确的结构体指针。

接下来,考虑到Qt多线程中的信号与槽的线程安全问题。由于RunExe类继承自QObject,并且在其中使用了信号和槽,需要确保信号与槽在正确的线程上连接。

可以尝试将信号与槽连接的方式更改为Qt的线程安全连接方式,即使用Qt::QueuedConnection作为连接方式:

connect(this, &RunExe::sigExeFinished, this, &TaskMonitor::onExeFinished, Qt::QueuedConnection);

这样可以确保在不同线程之间正确地传递信号和槽参数。

最后,确保在多线程环境下操作结构体指针和发射信号时进行适当的线程同步操作。这可以使用Qt的互斥锁和条件变量来实现。

RunExe类中添加一个QMutex成员变量,用于在访问结构体指针之前进行加锁操作:

class RunExe : public QObject, public QRunnable
{
    Q_OBJECT
    // ...
private:
    EXE *mParamer;
    QTableWidgetItem *pItem;
    bool isCancel;
    bool isFinished;
    QMutex mutex; // 添加互斥锁
};

然后,在成员函数内部使用互斥锁进行保护,以确保线程安全:

void RunExe::run()
{
    mutex.lock(); // 加锁
    // 访问结构体指针之前执行的操作
    mutex.unlock(); // 解锁

    // 其他操作...

    mutex.lock(); // 加锁
    // 访问结构体指针之后执行的操作
    mutex.unlock(); // 解锁

    // 发射信号...
}

这样可以确保在多线程环境下对结构体指针的访问是线程安全的。

希望这些方案能够帮助解决你的问题。如果还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^