繁体   English   中英

在QThread内的QCoreApplication上调用quit()时出错

[英]Error when calling quit() on a QCoreApplication within a QThread

为了从一个用Java编写的主应用程序调用的DLL中的一个单独线程中创建Qt事件循环,我根据在这里读到的建议做了以下工作,该工作很好:

// 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对象,该对象将自动创建一个在其中运行QCoreApplication的ToolThread:

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的来龙去脉,提出解决方案? 还是我设计此方法的根本缺陷?

这似乎对我有用...

首先,我向全局名称空间添加了一个静态的“清理”函数:

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会在事件循环仍在运行时调用cleanup函数。 这样可以安排QCoreApplication在状态良好并准备就绪后删除自身,同时将全局指针重置为NULL,以便应用程序的其余部分知道可以在需要时实例化新的QCoreApplication。

我想如果主应用程序立即创建一个新的QCoreApplication并尝试在旧的QCoreApplication仍在清理自身的过程中尝试在其上运行exec(),那么这可能会产生冲突。 我认为在我使用它的情况下不太可能发生这种情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM