【问题标题】:Error when calling quit() on a QCoreApplication within a QThread在 QThread 中的 QCoreApplication 上调用 quit() 时出错
【发布时间】:2014-08-12 07:38:15
【问题描述】:

为了在一个单独的线程中创建一个 Qt 事件循环,从一个由 Java 编写的主应用程序调用的 DLL 中,我做了以下工作,基于我读过的 here 的建议,它有效还不错:

// Define a global namespace. We need to do this because the parameters 
// passed to QCoreApplication must have a lifetime exceeding that of the 
// QCoreApplication object
namespace ToolThreadGlobal
{
    static int argc = 1;
    static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
    static QCoreApplication *coreApp = nullptr;
    static ToolThread *toolThread = nullptr;
};

//! The ToolThread class differs from a standard QThread only 
//! in its run() method
class ToolThread : public QThread
{
    //! Override QThread's run() method so that it calls 
    //! the QCoreApplication's exec() method rather than 
    //! the QThread's own
    void run() { ToolThreadGlobal::coreApp -> exec(); }
};

class ThreadStarter : public QObject
{
    Q_OBJECT

public:
    //! Constructor
    ThreadStarter()
    {
        // Set up the tool thread:
        if (!ToolThreadGlobal::toolThread)
        {
            ToolThreadGlobal::toolThread = new ToolThread();
            connect(ToolThreadGlobal::toolThread, &ToolThread::started,
                    this, &ThreadStarter::createApplication, Qt::DirectConnection);
                    // The thread's 'started' event is emitted after the thread
                    // is started but before its run() method is invoked. By 
                    // arranging for the createApplication subroutine to be 
                    // called before run(), we ensure that the required 
                    // QCoreApplication object is instantiated *within the 
                    // thread* before ToolThread's customised run() method 
                    // calls the application's exec() command.
            ToolThreadGlobal::toolThread->start();
        }
    }

    //! Destructor
    ~ThreadStarter()
    {
        // Ensure that the thread and the QCoreApplication are cleanly 
        // shut down:
        ToolThreadGlobal::coreApp -> quit();
        delete ToolThreadGlobal::coreApp;
        ToolThreadGlobal::coreApp = nullptr;
        delete ToolThreadGlobal::toolThread;
        ToolThreadGlobal::toolThread = nullptr;
    }

    //! Function to return a pointer to the actual tool thread:
    ToolThread* getThread() const { return ToolThreadGlobal::toolThread;  }

private:
    //! Create the QCoreApplication that will provide the tool 
    //! thread's event loop
    /*! This subroutine is intended to be called from the tool 
        thread itself as soon as the thread starts up.
    */
    void createApplication()
    {
        // Start the QCoreApplication event loop, so long as no 
        // other Qt event loop is already running
        if (QCoreApplication::instance() == NULL)
        {
            ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc, 
                                                             ToolThreadGlobal::argv);
        }
    }
};

要使用它,从主 Java 应用程序的线程调用的子例程只需要创建一个 ThreadStarter 对象 这将自动创建一个 ToolThread,其中运行一个 QCoreApplication:

itsThreadStarter = new ThreadStarter();
itsToolThread = itsThreadStarter -> getThread();

然后我们可以以通常的方式实例化一个 QObject 类,将其移至线程并使用 QMetaObject::invokeMethod 异步调用其方法:

itsWorker = new Worker();
itsWorker -> moveToThread(itsToolThread);

QMetaObject::invokeMethod(itsWorker, “doSomethingInteresting”);

当我们完成后,我们只需删除 ThreadStarter 对象,一切都被很好地清理了。除了恼人的消息说

WARNING: QApplication was not created in the main() thread

在启动时,它似乎满足了我的所有要求。

除了……(最后,这是我的问题)。

偶尔 - 到目前为止,我无法识别任何模式 - 我在关机过程中遇到错误。通常发生在行

        delete ToolThreadGlobal::coreApp;

但有时需要排队

    ToolThreadGlobal::coreApp -> exec();

(当然是在线程的 run() 方法中执行,直到 ToolThreadGlobal::coreApp -> quit(); 完全执行后才返回)。

错误消息通常是简单的访问冲突;有时它更复杂:

ASSERT failure in QObjectPrivate::deleteChildren(): "isDeletingChildren already set, did this function recurse?", file ..\qtbase\src\corelib\kernel\qobject.cpp, line 1927

我认为这是因为,一旦我向 QCoreApplication 发出 quit() 命令,我应该等待一段时间让它正确关闭事件循环,然后再删除它 - 就像人们通常会调用 quit()然后在一个普通的 QThread 上等待(),然后再删除它。但是,QCoreApplication 似乎没有等价的 wait() 命令,而且我无法实现 QTimer 来强制延迟,因为一旦我使用 quit() 关闭了事件循环,它就无法工作。因此,我不知所措。我有一个想法,因为 QCoreApplication 是一个 QObject,我可以调用它的 deleteLater() 方法,但我看不出我应该从哪里调用它。

是否有一位非常了解 QCoreApplication 和 QThread 来龙去脉的专家可以提出解决方案?还是我的设计方式存在根本缺陷?

【问题讨论】:

  • 你想达到什么目的?停止整个过程或删除Qt部分? quit() 停止整个过程,我不明白你为什么要在调用它之后做 anything 。我会假设 quit() 之后的任何删除操作都是多余的。 “isDeletingChildren 已设置”听起来就是这样。
  • 我想要的是在 Java 应用程序的其余部分继续运行时停止单独的 Qt 线程,同时让事情处于我可以稍后启动新 Qt 线程的状态。换句话说,我想达到这样一个点,即 ToolThreadGlobal 命名空间中的两个指针再次为空,而在此过程中没有任何错误消息被抛出。

标签: c++ multithreading qt qthread qcoreapplication


【解决方案1】:

这似乎对我有用...

首先,我在全局命名空间中添加了一个静态“清理”函数:

namespace ToolThreadGlobal
{
    static int argc = 1;
    static char * argv[] = { "MyVirtualMainApplication.exe", NULL };
    static QCoreApplication *coreApp = nullptr;
    static ToolThread *toolThread = nullptr;
    static void cleanup() { coreApp->deleteLater();  coreApp = nullptr; }
};

然后,从我的 ThreadStarter::createApplication 插槽中,我将 QCoreApplication 的 aboutToQuit 信号连接到它:

    void createApplication()
    {
        // Start the QCoreApplication event loop, so long as no other Qt event loop
        // is already running
        if (QCoreApplication::instance() == NULL)
        {
            ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc, 
                                                             ToolThreadGlobal::argv);
            connect(ToolThreadGlobal::coreApp, &QCoreApplication::aboutToQuit,
                    ToolThreadGlobal::cleanup);
        }
    }

然后'ThreadStarter'析构函数减少到只有五行(包括添加对 QThread::quit() 和 QThread::wait() 的调用,这应该是第一次出现):

    ~ThreadStarter()
    {
        // Ensure that the thread and the QCoreApplication are cleanly shut down:
        ToolThreadGlobal::coreApp -> quit();
        ToolThreadGlobal::toolThread -> quit();
        ToolThreadGlobal::toolThread -> wait();
        delete ToolThreadGlobal::toolThread;
        ToolThreadGlobal::toolThread = nullptr;
    }

当 ThreadStarter 析构函数调用 QCoreApplication::quit() 时,QCoreApplication 在其事件循环仍在运行时调用清理函数。这会安排 QCoreApplication 在它良好并准备好后自行删除,同时将全局指针重置为 NULL,以便应用程序的其余部分知道可以在需要时实例化新的 QCoreApplication。

我想如果主应用程序立即创建一个新的 QCoreApplication 并尝试在其上运行 exec() 而旧的 QCoreApplication 仍在清理自己的过程中,这会留下一个非常小的风险。我认为这在我使用它的环境中不太可能发生。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-28
    • 2012-11-02
    • 2022-01-14
    • 1970-01-01
    • 2020-12-06
    • 1970-01-01
    • 2010-11-16
    相关资源
    最近更新 更多