Qt槽没有在多线程DLL中执行

时间:2016-06-21 11:50:26

标签: c++ qt dll signals-slots

我正在开发一个动态加载的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!
}

1 个答案:

答案 0 :(得分:0)

感谢code_fodder提供的有用信息,找到了解决方案。

我使MyCommunicator成为CoreApplication的成员,在CoreApplication :: run方法中实例化它,并将QCoreApplication实例作为父实例,现在它可以工作了!