【问题标题】:How to stop a Qthread after calling movetothread()?调用 movetothread() 后如何停止 Qthread?
【发布时间】:2021-07-17 11:40:06
【问题描述】:

当我重新实现 QTcpserver::incomingConnection() 时,会创建一个新的 QThread 和一个新的 QTcpsocket。accept-fd 被传递给 QTcpsocket。但是,整个过程意外退出时会出现警告: QThread:在线程仍在运行时被销毁 当 Qthread 调用 quit() 时,这两个对象将被删除。这是下面的代码。

 void TcpServer::incomingConnection(qintptr handle)
{
    QThread* tcpThread = new QThread;
    TcpSocket* tcpSocket = new TcpSocket;

    tcpSocket->setSocketDescriptor(handle);
    tcpSocket->moveToThread(tcpThread);

    connect(tcpSocket,&TcpSocket::sigRecvFinish, [=](){
        tcpThread->quit();
        delete tcpSocket;
        delete tcpThread;
    });
    tcpThread->start();
}

如何退出线程并正确释放资源?同时,是什么导致了这个问题? 如果您能帮助我解决这个问题,我将不胜感激。

【问题讨论】:

    标签: c++ qt sockets thread-safety


    【解决方案1】:

    首先,Qt 有很好的异步 API,因此使用线程(在这种情况下)完全过时并且浪费资源。

    通过在连接语句中使用 lambda,您失去了线程跳跃功能,这是由 connect 默认完成的。结果是您试图从该对象管理的线程中销毁线程对象。

    请注意,QObject::connect 最后一个参数是:Qt::ConnectionType type = Qt::AutoConnection(没有 lambda 的连接)。当使用 lambda 时,QObject::connect 没有这样的功能,代码是从调用信号的线程调用的。

    删除 lambda 将确保每次调用都在正确的线程上完成。

    void TcpServer::incomingConnection(qintptr handle)
    {
        QThread* tcpThread = new QThread;
        TcpSocket* tcpSocket = new TcpSocket;
    
        tcpSocket->setSocketDescriptor(handle);
        tcpSocket->moveToThread(tcpThread);
    
        connect(tcpSocket, &TcpSocket::sigRecvFinish, tcpSocket, &QBject::delteLater);
        connect(tcpSocket, &TcpSocket::sigRecvFinish, tcpThread, &QThread::quit);
        connect( tcpThread, &QThread::finished, tcpThread, &QThread::deleteLater );
    
        tcpThread->start();
    }
    

    技术细节见:

    Qt Namespace | Qt Core 5.15.3

    Constant Value Description
    Qt::AutoConnection 0 (Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.

    【讨论】:

    • 感谢您的回答,代码可以在没有警告的情况下运行。还有两个问题。一、如何确保Qthread被正确删除,资源被释放?顺便说一句,为什么“删除”看起来很奇怪? Qt编程中销毁对象的正确方法是什么?Qt提供的对象需要删除时是否推荐使用QObject::deleteLater?
    • deleteLater 是一个插槽,因此它可以与Qt::AutoConnection 连接。这样,它不会测量从哪个胎面信号发出删除将在对象所属的线程上执行。这是在这里使用deleteLater 的唯一原因。我认为您应该更仔细地阅读我的答案并阅读链接文档。
    【解决方案2】:

    您需要将您的connect 替换为以下代码:

    connect( tcpSocket, &TcpSocket::sigRecvFinish, tcpSocket, &QObject::deleteLater );
    connect( tcpSocket, &QObject::destroyed, tcpThread, &QThread::quit );
    connect( tcpThread, &QThread::finished, tcpThread, &QThread::deleteLater );
    

    【讨论】:

    • UP:修复删除 tcpSocket 本身
    • 这是来自原始代码形式的 OP,是的,这很奇怪,但可以接受。
    猜你喜欢
    • 1970-01-01
    • 2013-10-18
    • 2013-04-21
    • 1970-01-01
    • 2013-12-17
    • 2021-06-11
    • 2021-06-04
    • 2017-01-28
    • 2018-11-10
    相关资源
    最近更新 更多