Я разрабатываю DLL, которая должна быть загружена динамически. В этой DLL мне нужно использовать некоторые сетевые компоненты Qt, такие как QLocalServer и QNetworkAccessManager. Чтобы использовать их, мне нужно запустить цикл событий Qt. Я читал, что QLocalServer можно использовать без цикла событий, но это не относится к QNetworkAccessManager AFAIK.Слоты Qt не выполняются в многопоточной DLL
Мне удалось создать и выполнить QCoreApplication. Выполнение и выполнение QCoreApplication выполняется в одном потоке, и я убедился, что QCoreApplication создается до того, как будет использован любой другой класс Qt.
DLL также запускает несколько других потоков, и когда я излучаю сигналы из этих потоков, их подключенные слоты никогда не вызываются, кроме случаев, когда тип соединения = Qt :: DirectConnection. Мне нужно избегать синхронных подключений, поэтому мне нужно использовать Qt :: QueuedConnection, правильно?
Другие темы, которые я упомянул, не являются QThread, они являются std :: thread или boost :: thread. Причина в том, что это общий код, который нужно запускать в приложениях, отличных от 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!
}
Как работает цикл событий в MyCommunicator? - Я вижу, что вы запускаете отдельный объект CoreApplication, но один из них не связан (или даже не знает) другого ... Вы можете добавить цикл QEventLoop; varaible в свой класс MyCommunicator и запустить его с помощью 'loop.exec() ; ... Проблема заключается в том, что вы передаете сигнал (queuedconnction), но он никогда не будет вызван, потому что нет цикла событий ... –
В качестве альтернативы вы можете передать CoreApplication (указатель) в качестве «родителя» вашего MyCommunicator и передать это до унаследованного QObject, который принимает QObject как параметр «parent» ... это предполагает, что ваш Класс CoreApplication является (или наследует) QCoreApplication? –
Вы сказали это сами: другие темы, которые вы используете, не являются QThreads, поэтому у них нет eventloop, и ваши слоты cannont не будут вызваны. Либо используйте QThread, либо создайте специальный eventloop. – Felix