我正在开发一个动态加载的DLL。在这个DLL中,我需要使用一些Qt网络组件,如QLocalServer和QNetworkAccessManager。要使用它们,我需要运行Qt事件循环。我已经读过QLocalServer可以在没有事件循环的情况下使用,但QNetworkAccessManager AFAIK的情况并非如此。
我设法创建并执行了QCoreApplication。 QCoreApplication的实例化和执行是在同一个线程中完成的,我确保在使用任何其他Qt类之前创建QCoreApplication。
DLL也运行其他几个线程,当我从这些线程发出信号时,它们的连接槽永远不会被调用,除非连接类型= Qt :: DirectConnection。我需要避免同步连接,所以我需要使用Qt :: QueuedConnection,对吧?
我提到的其他线程不是QThread,它们是std :: thread或者boost :: thread' s。原因是这是需要在非Qt应用程序中运行的共享代码。 信号以下列方式发出:我实例化一个从QObject派生的桥对象,并设置了Q_OBJECT,因此moc编译器从中生成信号/槽代码。从这个桥对象,我注册回调方法(使用增强信号)。当'其他'然后线程调用其中一个回调,然后桥对象发出一个信号,该信号连接到同一类中的一个槽。我们的想法是从Qt事件循环执行插槽,这样我就可以开始异步使用Qt网络类。但是从不调用插槽。为什么呢?
我已经删除了我的代码,以便在没有DLL的情况下重现问题。
的main.cpp
#include "bridge.h"
#include "worker.h"
#include <QDebug>
#include <memory>
#include <iostream>
#include <string>
struct MyLibrary
{
public:
MyLibrary()
: myWorker_()
, myQtBridge_(myWorker_)
{
myQtBridge_.start();
myWorker_.start();
}
private:
MyWorker myWorker_;
MyQtBridge myQtBridge_;
};
static std::shared_ptr<MyLibrary> myLibrary;
extern "C" __declspec(dllexport) void __cdecl start(void)
{
try {
myLibrary.reset(new MyLibrary());
} catch (const std::exception& e) {
qCritical() << e.what();
}
}
extern "C" __declspec(dllexport) void __cdecl stop(void)
{
try {
myLibrary.reset();
} catch (const std::exception& e) {
qCritical() << e.what() << '\n';
}
}
// main() is only here to reproduce the problem.
// In a DLL build, the calling application would call the start() and stop()
// functions.
int main(int argc, char *argv[])
{
Q_UNUSED(argc);
Q_UNUSED(argv);
start();
for (;;) {
std::cerr << "Enter q to quit: ";
std::string input;
if (std::getline(std::cin, input) && input == "q") {
break;
}
}
stop();
}
bridge.h
#ifndef BRIDGE_H
#define BRIDGE_H
#include "worker.h"
#include "communicator.h"
#include "qapp.h"
// BOOST includes
#include <boost/bind.hpp>
// Qt includes
#include <QDebug>
class MyQtBridge
{
public:
explicit MyQtBridge(MyWorker& myWorker)
: myWorker_(myWorker) // copy reference to the worker
, coreApplication_() // instantiate QtCoreApplication and exec() it in a thread
, myCommunicator_() // instantiate my Qt communication module
{
myWorker_.onSignal1(boost::bind(&MyQtBridge::onSignal1Handler, this));
}
void start()
{
coreApplication_.start();
}
private:
void onSignal1Handler()
{
qDebug() << "MyQtBridge: calling myCommunicator_.signal1()";
myCommunicator_.signal1();
qDebug() << "MyQtBridge: called myCommunicator_.signal1()";
}
private:
MyWorker& myWorker_;
CoreApplication coreApplication_; // Must be created before MyCommunicator!
MyCommunicator myCommunicator_;
};
#endif // BRIDGE_H
worker.h
#ifndef WORKER_H
#define WORKER_H
// BOOST includes
#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
// STL includes
#include <memory>
// Qt includes
#include <QDebug>
// A dummy worker, just to reproduce the problem
// This code cannot have any dependencies to Qt ( except for QDebug now,... :-D )
class MyWorker
{
public:
typedef boost::signals2::signal<void ()> signal_1_type;
MyWorker()
{
}
// called from main thread
~MyWorker()
{
try {
if (thread_) {
thread_->interrupt();
thread_->join();
qDebug() << "MyWorker thread joined";
thread_.reset();
}
} catch (const std::exception& e) {
qCritical() << e.what();
}
}
boost::signals2::connection onSignal1(const signal_1_type::slot_type& subscriber)
{
return signal_1_.connect(subscriber);
}
void start()
{
if (!thread_) {
thread_.reset(new boost::thread(boost::bind(&MyWorker::run, this)));
qDebug() << "MyWorker thread created";
}
}
private:
void run()
{
for (;;) {
boost::this_thread::interruption_point();
boost::this_thread::sleep(boost::posix_time::seconds(3));
qDebug() << "MyWorker: calling signal_1_()";
signal_1_();
qDebug() << "MyWorker: called signal_1_()";
}
}
private:
std::shared_ptr<boost::thread> thread_;
signal_1_type signal_1_;
};
#endif // WORKER_H
qapp.h
#ifndef QAPP_H
#define QAPP_H
#include <QCoreApplication>
#include <QDebug>
#include <thread>
#include <mutex>
#include <condition_variable>
// Purpose of this class is to get a Qt event loop going.
// Instantiation of the QCoreApplication and calling it's exec() method
// are both done in the same thread (seems to be a requirement).
// The rest of this class is synchronization.
class CoreApplication
{
public:
CoreApplication()
: thread_(&CoreApplication::run, this)
{
// Wait until the QCoreApplication has been created
// This is needed before any other Qt objects are created.
std::unique_lock<std::mutex> lock(mutex_);
cv_app_created_.wait(lock);
}
CoreApplication(const CoreApplication&) = delete;
~CoreApplication()
{
QCoreApplication::instance()->quit();
thread_.join();
}
void start()
{
cv_started_.notify_all();
}
private:
void run()
{
int argc = 0;
char **argv = nullptr;
QCoreApplication app(argc, argv);
qDebug() << "QCoreApplication instantiated";
cv_app_created_.notify_all();
// Wait until we're started
{
std::unique_lock<std::mutex> lock(mutex_);
cv_started_.wait(lock);
}
// blocking call, should return when QCoreApplication::instance()->quit() is called
qDebug() << "CoreApplication:: calling QCoreApplication::exec()";
app.exec();
qDebug() << "CoreApplication:: called QCoreApplication::exec()";
}
private:
std::thread thread_;
std::mutex mutex_;
std::condition_variable cv_app_created_, cv_started_;
};
#endif // QAPP_H
communicator.h
#ifndef COMMUNICATOR_H
#define COMMUNICATOR_H
// Qt includes
#include <QObject>
// This would be the class that uses the Qt networking classes
// It would operate independently, reacting only to signals.
class MyCommunicator : public QObject
{
Q_OBJECT
public:
MyCommunicator();
~MyCommunicator();
// called from MyQtBridge::onSignal1Handler()
void signal1();
signals:
void sigSignal1();
private slots:
void slotSignal1();
};
#endif // COMMUNICATOR_H
communicator.cpp
#include "communicator.h"
// Qt includes
#include <QDebug>
MyCommunicator::MyCommunicator()
{
// Note: the reason for this local signal connection is that
// the signal sigSignal1() is emitted from a
// different thread. The Qt::QueuedConnection flag should make sure that
// the slot slotSignal1() is called in the QCoreApplication thread
auto rc = connect(
this, SIGNAL(sigSignal1())
, this, SLOT(slotSignal1())
, Qt::QueuedConnection
);
qDebug() << "MyCommunicator: connect: " << rc;
}
MyCommunicator::~MyCommunicator()
{
}
// called from MyQtBridge::onSignal1Handler()
void MyCommunicator::signal1()
{
qDebug() << "MyCommunicator: emitting sigSignal1()";
emit sigSignal1();
qDebug() << "MyCommunicator: emitted sigSignal1()";
}
void MyCommunicator::slotSignal1()
{
qDebug() << "MyCommunicator: slotSignal1(), yay!"; // NEVER CALLED!
}
答案 0 :(得分:0)
感谢code_fodder提供的有用信息,找到了解决方案。
我使MyCommunicator成为CoreApplication的成员,在CoreApplication :: run方法中实例化它,并将QCoreApplication实例作为父实例,现在它可以工作了!