使用 VS2019 构建时,Qt5qml 应用程序崩溃

Application crash with Qt5qml when building with VS2019

我有一个用 VS2013 和 Qt 5.9.6 编译的应用程序,它工作正常。现在我想升级Qt版本到5.15.2并用VS2019编译它。构建成功,我可以运行应用程序但它总是在Qt5Qml模块崩溃.

调试了很久,还是不知道为什么。也许5.15.2与VS2019不完全兼容?

2020 年 4 月 20 日更新: 我有一个 class 叫做 ExpressionEvaluator

class ExpressionEvaluator
{
    public:
        ExpressionEvaluator();
        virtual ~ExpressionEvaluator();

        bool evaluate(const QString &expression, const QVariantMap &values);

    protected:
        class ExpressionEvaluatorPrivate *d_ptr;
        Q_DECLARE_PRIVATE(ExpressionEvaluator);
        int gcCounter;
};

这是 ExpressionEvaluatorPrivateExpressionEvaluator

的定义
class ExpressionEvaluatorPrivate
{
    public:
        QJSEngine engine;
        QMutex mutex;
};

ExpressionEvaluator::ExpressionEvaluator()
    : d_ptr(new ExpressionEvaluatorPrivate()),
      gcCounter(0)
{}

ExpressionEvaluator::~ExpressionEvaluator()
{
    Q_D(ExpressionEvaluator);
    d->engine.collectGarbage();

    delete d_ptr;
}

bool ExpressionEvaluator::evaluate(const QString &expression, const QVariantMap &values)
{
    Q_D(ExpressionEvaluator);

    QMutexLocker locker(&d->mutex);
    // CRASH HERE
    for (auto it = values.begin(); it != values.end(); ++it) {
        d->engine.globalObject().setProperty(it.key(), d->engine.toScriptValue(it.value())); // <---CRASH HERE
    }

    QJSValue qjsValue = d->engine.evaluate(expression);

    bool ok = qjsValue.toBool();

    if (++gcCounter >= GC_CALL_LIMIT) {
        d->engine.collectGarbage();
        gcCounter = 0;
    }

    for (auto it = values.begin(); it != values.end(); ++it) {
        d->engine.globalObject().deleteProperty(it.key());
    }

    return ok;
}

经过很长时间的调试,这让人头疼。我终于找到了解决方案。 (但我不清楚它为什么有效 :))。我只想post在这里回答我的问题,所以如果有人像我一样遇到这个问题可以找到解决方案。

如你所见,我有一个函数叫ExpressionEvaluator::evaluate,这个函数会被多线程涉及到。使用 Qt 5.9.6 一切正常,但是当我升级到 Qt 5.15.2 时,应用程序经常崩溃并且堆栈跟踪指向 QJsEngine 附近的行 d->engine.globalObject() 对于我的研究,也许这就是问题所在:

  • QJsEngine 有一个名为 gc 的函数用于垃圾收集
  • gc 将 运行 拥有引擎的线程。
  • 每当另一个线程正在尝试访问 globalObject()QJsEngine 的任何函数(如 evaluate())并且如果同时调用 gc => 崩溃

是的,正如我之前所说,我并不真正理解这个问题,但根据这个猜测,我能够创建一个解决方案并且它有效!

这是我的解决方案:

  • 我创建了另一个线程(事件循环线程),QJsEngine 将在该线程中运行(同时它的所有功能都将在该线程中执行)
  • 当另一个线程调用时 evaluate() 它将发出一个信号,并且该信号连接到插槽(插槽将完成工作 - 当然,该插槽仅在一个线程中执行)
  • 此解决方案确保:gc所有函数都将在同一个线程上执行。

P/s:抱歉,如果我的解释难以理解,但这个问题有点具体,并且坚持我的应用程序,所以这是我能做的最好的。但对于摘要:make sure all the function of QJsEngine is called in the same thread