Search code examples
c++qtrpcsignals-slotsdata-distribution-service

In a Qt signal-slot programm,The slot function was not executed after sent


I am writing an RPC-like program that uses the Qt signal slot, when using a client program to call the server, a function is executed, and I need to print the value of this function on the QtableWidget, so I use emit to transmit signals in this function, but I find that the slot function is not executed. This seems to be related to multithreading.

update: I found that I made a mess of the code, here to simplify. I use a static class method to emit a signal so the code looks a little bit tricky.

// service.cpp file
#include "service.h"
multipleService* multipleService::getInstance = nullptr;
int add(int a, int b) {
    return a + b;
}
multipleService::multipleService() {
    getInstance = this; // for static method to emit signal
    bool ok = connect(this, &multipleService::internalMessageOutput, this, &multipleService::messageOutputFunc, Qt::AutoConnection);
}
// implement service
void multipleService::AddService(ServiceType::Request& request, ServiceType::Response& response) {
// do some stuff ... get response
emit getInstance->internalMessageOutput(QString(response));
}
void multipleService::messageOutputFunc(QString msg) {
    emit getInstance->messageOutput(msg);
}
// service.h file
#pragma once
#include <stdio.h>
#include <string.h>
// some declarations
class multipleService :public QObject {
    Q_OBJECT
public:
    multipleService();
    ~multipleService();
    static void AddService(ServiceType::Request& request, ServiceType::Response& response);
    void messageOutputFunc(QString);
private:
    static multipleService* getInstance;
signals:
    void messageOutput(QString);
    void internalMessageOutput(QString);
};

And in the mainwindow.cpp file, I use create_service to behave like a server in RPC. And this actually works fine. once emit getInstance->internalMessageOutput(QString(response)); executes,the signal func executes.

//mainwindow.cpp in main thread
multipleService* s = new multipleService();
connect(s, &multipleService::messageOutput, this, &serviceThread::outputMessage);
auto service = node.create_service<ServiceType>(service_name, multipleService::AddService, qoS);

But I want to create a multithread so I changed the code like

// mainwindow.cpp file in main thread
QThread* thread = new QThread; //create a thread 
serviceThread* myThread = new serviceThread(this);// create a class which derives from QObject and move it to the thread
//connect start signal with a lambda func
connect(thread, &QThread::started, myThread, [&myThread, SERVICE_NAME]() 
{  std::string a = SERVICE_NAME;
   char* AddServiceName = (char*)a.data();
   myThread->createService("NODE", AddServiceName, 10);
});
// start a thread and this will call the slot func
thread->start();
myThread->moveToThread(thread);

I create a serviceThread.cpp and new serviceThread and move it to a thread to support multithread but emit getInstance->internalMessageOutput(QString(response)),the slot func doesn't execute.

//serviceThread.cpp file 
void serviceThread::createService(const char* nodeName, const char* service_name, int historyDepth) {
// do some declaration
   multipleService* s = new multipleService();
   connect(s, &multipleService::messageOutput, this, 
&serviceThread::outputMessage);
auto service = node.create_service<ServiceType>(service_name, 
   multipleService::AddService, qos);
if (service) {
        service->start();
}
else {
    // error
    return;
}
    while (true)
    {  // 
        if (QThread::currentThread()->isInterruptionRequested())
        {
            break;
        }
    }

}

I have tried to change the connect type to Qt::QueuedConnection and this also failed. I also set a breakpoint on the line and it literally executed.

update: OK,I find something wrong with the while loop in the multithread, I use QCoreApplication::processEvents(QEventLoop::AllEvents, 10); in the loop to temporirally solve this problem. Are there any other solutions?


Solution

  • One of problems I see right away is mainwindow.cpp code:

    QThread* thread = new QThread; //create a thread 
    serviceThread* myThread = new serviceThread(this);// create a class which derives 
    ...
    myThread->moveToThread(thread); // this will silently fail.
    

    If you look up documention on website, you must pay attention, they mentioned that. You can't move a parented QObject and you cannot move a QWidget at all. Since serviceThread is a QObject, myThread cannot be moved - it is parented to main window now, which is part of main thread.

    My usual approach to this is

    • have an QObject-derived class, let's call it Manager,
    • move Manager to the worker thread,
    • connecting appropriate signals of QThread to Managers slots.
    • Manager would create the rest of objects
    • and act as a proxy and a parent to his children.

    Code for an old project, redacted.. sorry for Qt4 style connect()

    void QApp::startThreads()
    {
        // Just basic thread is enough here
        netThread = new QThread(this); 
        manager = new Manager();
        
        // IMPORTANT: Manager does not have a parent
        // 
        // 1.moveToThread first
        //
        manager->moveToThread(netThread); 
        //
        // 2. connect later. moveToThread breaks it.
        //
        // run main function when thread started
        connect(netThread, SIGNAL(started()), manager, SLOT(start()));
        // delete everything when we stop thread. 
        connect(netThread, SIGNAL(finished()), manager, SLOT(deleteLater()));
    
        // Something Manager-specific, just for an example
        connect(manager, SIGNAL(receivedData()), this, SLOT(updateAll()) );
        connect(manager, SIGNAL(receivedStatus()), ConData, SLOT(onReceive()));
    
        netThread->start();
    }
    

    What Manager::start does:

    void Manager::start()
    {
       // 1. create children objects
       // 2. connect signals
    }