我的应用程序内部有一个带有2个状态(连接/断开连接)的Qml按钮。这是各自的代码:
Button {
id : connectDisconnectButton
anchors.centerIn: parent
property bool isConnected : false
text : isConnected ? qsTr("Disconnect") : qsTr("Connect")
antialiasing: true
property var currentConnectionParams : ({})
onClicked: {
if ( isConnected ) {
proxy.disconnectFromEmulatorService();
} else {
connectDisconnectButton.currentConnectionParams["port"] = serviceConnectionPort.getValue();
connectDisconnectButton.currentConnectionParams["ip_addr"] = String(serviceConnectionIpPart_0.getValue())
+ String(".") + String(serviceConnectionIpPart_1.getValue())
+ String(".") + String(serviceConnectionIpPart_2.getValue())
+ String(".") + String(serviceConnectionIpPart_3.getValue());
proxy.connectToEmulatorService( connectDisconnectButton.currentConnectionParams );
}
isConnected = !isConnected;
}
}
我看到的是,我不时调用C ++代理的connectToEmulatorService方法时得到了SIGSEGV。这是相应的c ++代码:
Q_INVOKABLE void connectToEmulatorService( QVariant in )
{
m_serviceConnectionParams = in;
emit connectionInitiatedSignal();
m_functorExecutor->executeFunctor( boost::bind(&EmulatorControlProxy::connectToEmulatorServiceImpl, this ) );
}
void connectToEmulatorServiceImpl()
{
QMap<QString, QVariant> connectionParams_ = m_serviceConnectionParams.toMap();
try {
emit connectionOkSignal();
}catch(...) {
Error("connectToEmulatorServiceImpl failure : ip = %s, port = %s",
connectionParams_["ip_addr"].toString().toStdString().c_str(),
connectionParams_["port"].toString().toStdString().c_str() );
emit connectionFailedSignal();
}
}
GDB回溯提供以下输出:
(gdb) backtrace
#0 0x00007ffff6e0777b in QV4::QObjectWrapper::virtualGet(QV4::Managed const*, QV4::PropertyKey, QV4::Value const*, bool*) ()
at /home/developer/Qt5.12.0/5.12.0/gcc_64/lib/libQt5Qml.so.5
#1 0x00007ffff6e8802b in QV4::Runtime::method_loadProperty(QV4::ExecutionEngine*, QV4::Value const&, int) () at /home/developer/Qt5.12.0/5.12.0/gcc_64/lib/libQt5Qml.so.5
#2 0x00007fffe0003d9a in ()
#3 0x0000000000000000 in ()
这可能是什么原因?如何克服呢? Qt 5.12 Ubuntu 18.10 g ++ 7.3
PS。问题与
有关QMap<QString, QVariant> connectionParams_ = m_serviceConnectionParams.toMap();
似乎在functorExecutor线程尝试访问它之前,已删除了临时QML对象映射。但是,如果我将传入的QVariant复制到类成员变量,为什么会发生这种情况?作为一种解决方法,我在调用C ++之前为我的代理声明了一堆Q_PROPERTY,以在其中存储QML数据。这似乎可行。但我相信Qt中存在某个问题。任何帮助表示赞赏。
答案 0 :(得分:0)
这是Qt-JIRA的官方回复:
“您正在从两个不同的线程访问您的m_connectionParams而不进行锁定。主线程在connectToEmulatorService()中写入它,而工作线程在connectToEmulatorServiceImpl()中对其进行读取。如果在工作线程刚刚读取时主线程再次进行了写入,在之前的迭代中,您会崩溃。这是最明显的问题。
但是,即使您确实锁定了互斥锁来访问m_connectionParams,这仍然是不安全的。您拥有的变体内部有一个QJSValue。为了将其转换为地图,我们必须深入调用JavaScript引擎,构造作用域,并与JavaScript堆进行交互。如果其他JavaScript同时在另一个线程中运行,则您会遇到麻烦。
只需在主线程中提取QVariantMap并将其作为参数传递给函子即可。”
因此,拥有QVariant副本是不够的……它仅位于Qt源码内部。