首页 > 其他分享 >未使用 `deleteLater` 而直接使用 `delete` 导致问题

未使用 `deleteLater` 而直接使用 `delete` 导致问题

时间:2024-11-16 12:20:16浏览次数:1  
标签:对象 worker deleteLater Controller 线程 使用 delete

以下是一个完整的Qt代码示例,展示了未使用 deleteLater 而直接使用 delete 导致问题的情况,该示例涉及到一个简单的多线程场景,主线程创建一个工作线程,工作线程完成任务后通知主线程,在对象删除处理不当的情况下会出现崩溃等问题。

示例代码

#include <QObject>
#include <QThread>
#include <QDebug>
#include <QCoreApplication>

// 工作类,继承自QObject,用于在工作线程中执行任务
class Worker : public QObject
{
    Q_OBJECT
public:
    Worker() {}

signals:
    // 任务完成后发送信号,传递一个整数值给主线程
    void resultReady(int result);

public slots:
    void doWork()
    {
        // 模拟耗时任务,这里简单睡眠一下
        QThread::sleep(2);
        int result = 42;
        // 发送任务完成的信号
        emit resultReady(result);
    }
};

// 控制器类,用于管理工作线程和处理结果
class Controller : public QObject
{
    Q_OBJECT
public:
    Controller()
    {
        // 创建工作对象和线程对象
        worker = new Worker();
        thread = new QThread();

        // 将工作对象移动到工作线程中
        worker->moveToThread(thread);

        // 连接线程启动信号和工作对象的工作函数
        connect(thread, &QThread::started, worker, &Worker::doWork);

        // 连接工作对象的完成信号和自身的处理结果函数
        connect(worker, &Worker::resultReady, this, &Controller::handleResult);

        // 启动线程
        thread->start();
    }

    ~Controller()
    {
        // 这里直接delete工作对象,会导致问题,应该使用worker->deleteLater()
        delete worker;
        thread->quit();
        thread->wait();
        delete thread;
    }

private slots:
    void handleResult(int result)
    {
        qDebug() << "Received result: " << result;
    }

private:
    Worker *worker;
    QThread *thread;
};

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    {
        Controller controller;
    }
    return app.exec();
}

#include "main.moc"

问题分析

在上述代码中,Controller 类负责管理工作线程和处理工作线程返回的结果。在 Controller 的析构函数中,直接使用 delete 来删除 worker 对象存在以下严重问题:

  • 对象生命周期冲突
    Controller 的析构函数被调用时(例如在 main 函数中 Controller 实例所在的作用域结束时),如果此时工作线程中的 worker 对象还在执行 doWork 函数(比如还处于 QThread::sleep(2) 的睡眠阶段或者刚结束睡眠正准备发送 resultReady 信号),直接调用 delete 去删除 worker 对象,就违背了对象的正常生命周期。因为 worker 对象在工作线程中仍处于活动状态,其内部资源还在被使用,这样强制删除会导致工作线程后续可能对已经释放的内存进行访问(例如尝试去发送 resultReady 信号时,相关的对象内存已经不存在了),从而引发程序崩溃,一般会出现类似“段错误”的报错提示。

  • 线程安全问题
    由于是在多线程环境下,主线程(也就是 Controller 所在的线程,执行析构函数的线程)直接删除了 worker 对象,而工作线程并不知道这个情况,这就造成了线程间的同步问题。如果工作线程后续继续按照正常流程去操作 worker 对象(比如触发信号等),就会出现对非法内存区域的访问,破坏了多线程编程中对共享对象访问的安全性要求,导致程序出现未定义行为甚至崩溃。

正确的做法应该是在合适的时机调用 worker->deleteLater(),例如可以在 handleResult 槽函数中判断任务已经彻底完成,不会再有对 worker 对象的操作后,调用 worker->deleteLater() 来安全地安排 worker 对象的删除,让Qt的事件循环在确保安全的情况下(比如工作线程相关的所有操作都完成后)再执行删除操作,避免上述出现的各种问题,保障程序的稳定运行。

希望这个示例能够清晰地展示未合理使用 deleteLater 所带来的问题,你如果还有其他疑问或者想进一步了解相关知识,可以随时问我。

标签:对象,worker,deleteLater,Controller,线程,使用,delete
From: https://www.cnblogs.com/DesertCactus/p/18549249

相关文章

  • bloompy库的CountingBloomFilter使用说明及示例
    1、使用说明: HelponclassCountingBloomFilterinmodulebloompy:classCountingBloomFilter(BloomFilter) | CountingBloomFilter(error_rate=0.001,element_num=10000,bit_num=None) |  | Methodresolutionorder: |   CountingBloomFilter ......
  • thinkphp6 使用自定义命令,生成数据库视图
    在ThinkPHP命令行工具中,你可以为选项设置别名,通过为选项指定一个简短的别名来简化命令输入。例如,如果你希望--force-recreate选项有一个简短的别名-f,你可以通过在addOption方法中设置第二个参数来实现这一点。示例:为选项设置别名在addOption方法的第二个参数中设置别......
  • 使用 Tcl 实现滑动验证码识别
    滑动验证码是一种常见的验证方式,用于验证用户操作的真实性。以下是使用Tcl实现滑动验证码识别的简单示例。功能概述程序的主要功能包括:读取滑动验证码的图片。分析滑块与缺口位置。模拟滑块移动,完成验证。代码实现tcl引入必要的库packagerequireImgpackagerequire......
  • 使用 Chapel 实现滑动验证码识别
    滑动验证码识别是一种基于图像处理的技术,用于识别滑块的缺口位置。本篇文章将演示如何使用Chapel语言实现一个简单的滑动验证码识别程序。Chapel简介Chapel是一种并行编程语言,适合数据密集型计算任务。我们可以利用其强大的数组和数据处理能力完成图像分析。环境准备安装Ch......
  • 使用nvm管理多版本node的详细教程
    在开发工作中,经常在不同的项目中使用不同版本的node去开发,换一个项目在重新安装node太麻烦,所以使用nvm来管理多版本的node开发环境,就非常方便了,所以本文给大家介绍了如何使用nvm管理多版本node,需要的朋友可以参考下 前言在开发工作中,经常在不同的项目中使用不同版本的n......
  • Gin连接使用COS
    packagestorageimport("context""WchimeGinSystem/conf""WchimeGinSystem/utils""io""log""net/http""net/url""path/filepath""st......
  • 使用 Neko 编程语言实现简单的滑动验证码识别
    滑动验证码是一种常见的安全验证方式,要求用户将图块拖动到正确位置。本文将使用Neko编程语言实现一个简单的滑动验证码识别程序,通过基本的图像处理技术自动识别图块匹配位置。实现步骤加载图片:使用Neko的图像处理库加载滑块和背景图片。图像预处理:转换为灰度图并进行边缘......
  • gin使用JWT验证
    packagejwtauthimport("WchimeGinSystem/conf""errors""time""github.com/golang-jwt/jwt/v5")typeMyClaimsstruct{jwt.RegisteredClaimsUserIdint64}funcCreateToken(userIdint64)......
  • chainWebpack: config => { // 移除 preload(预载) 插件 config.plugins.dele
    在VueCLI项目中,chainWebpack是一个用于自定义Webpack配置的钩子。通过chainWebpack,你可以对Webpack配置进行更细粒度的控制。你提到的代码片段的作用是移除preload和prefetch插件。下面是对这段代码的详细解释:代码解析chainWebpack:config=>{//移除preload......
  • sqlmap使用教程
    参考文章:SQLmap使用教程图文教程(超详细)-CSDN博客可使用必应搜索以下网站来测试使用:inurl:news.asp?id=site:edu.cninurl:news.php?id=site:edu.cninurl:news.aspx?id=site:edu.cnhttps://i-blog.csdnimg.cn/blog_migrate/80d2a4ffaa0cb7d7d1db82770ccdf1cf.png扫描完......