Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Need Help with getting a slot executed in the correct thread

Need Help with getting a slot executed in the correct thread

Scheduled Pinned Locked Moved Solved General and Desktop
qtcpsocketqtcpservernetworkserver - clientqthreadpool
8 Posts 3 Posters 3.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    Curtwagner1984
    wrote on 17 Apr 2018, 12:59 last edited by
    #1

    Greetings.

    I'm trying to implement a TCP server with a thread pool. Meaning that for each incoming connection to the server the server instantiates a runnable class that handles the communications with the client until the connection is closed, this runnable is then passed to a QTheardpool class that runs it.

    I'm using QTCPSocket's signal readyRead to notify the runnable that it has to read from the socket's buffer. However, it seems that because the runnable class was instantiated in the main thread, the reading is done in the main thread itself. I would like the reading to be done in the thread that runs the runnable.

    My ServerRunnable class also inherits from QObject in order to be able to use an event loop and I used QEventLoop to prevent the run function from returning until all communications with the server are done.

    Server.cpp code:

    
    Server::Server(QTcpServer *parent) : QTcpServer(parent)
    {
    
        this->threadPool = new QThreadPool(parent);
        this->threadPool->setMaxThreadCount(8);
    
    }
    
    void Server::startServer()
    {
        qDebug() << tr("Thread %1: Starting server ...").arg(int(QThread::currentThreadId()));
    
    
        if(!this->listen(QHostAddress::LocalHost,4000)){
            qDebug() << tr("Server could not start because '%1' ").arg(this->errorString());
        }
    
        qDebug() << tr("Server started and is listening at %1:%2").arg(this->serverAddress().toString()).arg(this->serverPort());
    
    
    
    
    
    }
    
    void Server::incomingConnection(qintptr handle)
    {
    
        qDebug() << tr("Thread %1: incomingConnection funciton in Server has been called....").arg(int(QThread::currentThreadId()));
    
    
        ServerRunnable *runnable = new ServerRunnable(handle);
    
        runnable->setAutoDelete(true);          // This is true by default.
    
        this->threadPool->start(runnable);
    
    
    
    
    }
    
    
    

    serverrunnable.cpp:

    
    #include "serverrunnable.h"
    
    
    
    ServerRunnable::ServerRunnable(qintptr socketHandle)
    {
        this->socketHandel = socketHandle;
    }
    
    void ServerRunnable::run()
    {
    
        this->init();
    
    
        qDebug() << tr("Runnable %1 started. Running...").arg(int(QThread::currentThreadId()));
    
        qDebug() << tr("Runnable %1 connected to %2:%3").arg(int(QThread::currentThreadId())).arg(this->socket->peerAddress().toString()).arg(this->socket->peerPort());
    
        qDebug() << tr("Waiting for client input...");
    
    
    
        this->loop->exec();
    
        qDebug() << tr("Thread: %1: End of run funciton.").arg(int(QThread::currentThreadId()));
    
    
    
    
    
    }
    
    void ServerRunnable::init()
    {
    
    
        this->socket = new QTcpSocket();
    
        this->loop = new QEventLoop();
    
    
        this->socket->setSocketDescriptor(this->socketHandel);
    
    
        connect(this->socket,&QTcpSocket::disconnected,this,&ServerRunnable::onSocketDisconnected,Qt::QueuedConnection);
        connect(this->socket,&QTcpSocket::readyRead,this,&ServerRunnable::onReadReady,Qt::QueuedConnection);
    
    }
    
    void ServerRunnable::onReadReady()
    {
        qDebug() << tr("Thread: %1 :Got read ready signal from socket...").arg(int(QThread::currentThreadId()));
    
        QByteArray ba;
    
        ba = this->socket->readAll();
    
        qDebug() << tr("Client message: %1").arg(QString(ba));
    
        qDebug() << tr("Echoing message back to client...");
    
        this->socket->write(ba);
    
        this->socket->flush();
    
        this->socket->waitForBytesWritten();
    
        qDebug() << tr("Message sent to client...");
    
    
    
    }
    
    void ServerRunnable::onSocketDisconnected()
    {
        qDebug() << tr("Thread: %1 :Socket was disconnected... Exiting...").arg(int(QThread::currentThreadId()));
        this->shouldExit = true;
        this->socket->close();
        this->socket->deleteLater();
        this->loop->exit();
        this->loop->deleteLater();
    
    
    //    this->eventLoopQuit();
    
    
    }
    
    
    

    This is the output when I run the server, connect to it and send messages:

    "Thread 15096: Starting server ..."
    "Server started and is listening at 127.0.0.1:4000"
    "Thread 15096: incomingConnection funciton in Server has been called...."
    "Runnable 12048 started. Running..."
    "Runnable 12048 connected to 127.0.0.1:52818"
    "Waiting for client input..."
    "Thread: 15096 :Got read ready signal from socket..."
    "Client message: Hello Server\r\n"
    "Echoing message back to client..."
    QObject: Cannot create children for a parent that is in a different thread.
    (Parent is QNativeSocketEngine(0x280d938), parent's thread is QThread(0x280d828), current thread is QThread(0x2800570)
    "Message sent to client..."
    "Thread: 15096 :Got read ready signal from socket..."
    "Client message: Hello again server!\r\n"
    "Echoing message back to client..."
    "Message sent to client..."

    The main thread is 15096, and when a new connection occurs the server correctly opens a new thread 12048. However, the thread that executes the slot for readyRead is the main thread (15096) even though the connection is made in the init() function in the thread 12048.

    Can someone point me to what am I doing wrong, and how do I make the slot execute from the correct thread?

    Thank you!

    1 Reply Last reply
    0
    • G Offline
      G Offline
      Gojir4
      wrote on 17 Apr 2018, 13:57 last edited by
      #2

      Hello,
      I think only the run() method is executed in a different thread, but the instance of ServerRunnable remains in the main thread (where it has been created), and this is why readyRead() is called in the main thread.

      You can have the same issue with QThread, see this from documentation:
      "It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread."

      A solution could be to use the "worker-object" approach, using QObject::moveToThread() as described in the QThread documentation. With this solution you are moving the entire QObject's instance into a different thread, so the slots are executed in the right thread.

      C 1 Reply Last reply 17 Apr 2018, 14:53
      3
      • V Offline
        V Offline
        VRonin
        wrote on 17 Apr 2018, 14:05 last edited by VRonin
        #3

        You cant with QTheardPool, only QThread can manage correctly signal and slots, see http://doc.qt.io/qt-5/threads-technologies.html#choosing-an-appropriate-approach

        Close-to-manadatory reading: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        1 Reply Last reply
        3
        • G Gojir4
          17 Apr 2018, 13:57

          Hello,
          I think only the run() method is executed in a different thread, but the instance of ServerRunnable remains in the main thread (where it has been created), and this is why readyRead() is called in the main thread.

          You can have the same issue with QThread, see this from documentation:
          "It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread."

          A solution could be to use the "worker-object" approach, using QObject::moveToThread() as described in the QThread documentation. With this solution you are moving the entire QObject's instance into a different thread, so the slots are executed in the right thread.

          C Offline
          C Offline
          Curtwagner1984
          wrote on 17 Apr 2018, 14:53 last edited by
          #4

          @Gojir4 said in Need Help with getting a slot executed in the correct thread:

          A solution could be to use the "worker-object" approach, using QObject::moveToThread() as described in the QThread documentation. With this solution you are moving the entire QObject's instance into a different thread, so the slots are executed in the right thread.

          Thanks, This sounds like the right solution. But given the fact that the worker object doesn't have its own event loop (exec():) how will I go about preventing it from exiting before the connection is closed?

          In my previous implementation using QThreads I just did something like this:

          void SocketThread::run()
          {
              qDebug() << tr("Thread %1 Started ...").arg(int(QThread::currentThreadId()));
          
              exec();
          
          
          }
          

          How can I achieve similar behavior in the worker object? Do I need to use QEventLoop?

          V 1 Reply Last reply 17 Apr 2018, 15:01
          0
          • C Curtwagner1984
            17 Apr 2018, 14:53

            @Gojir4 said in Need Help with getting a slot executed in the correct thread:

            A solution could be to use the "worker-object" approach, using QObject::moveToThread() as described in the QThread documentation. With this solution you are moving the entire QObject's instance into a different thread, so the slots are executed in the right thread.

            Thanks, This sounds like the right solution. But given the fact that the worker object doesn't have its own event loop (exec():) how will I go about preventing it from exiting before the connection is closed?

            In my previous implementation using QThreads I just did something like this:

            void SocketThread::run()
            {
                qDebug() << tr("Thread %1 Started ...").arg(int(QThread::currentThreadId()));
            
                exec();
            
            
            }
            

            How can I achieve similar behavior in the worker object? Do I need to use QEventLoop?

            V Offline
            V Offline
            VRonin
            wrote on 17 Apr 2018, 15:01 last edited by
            #5

            @VRonin said in Need Help with getting a slot executed in the correct thread:

            Close-to-manadatory reading: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            C 1 Reply Last reply 17 Apr 2018, 15:28
            0
            • V VRonin
              17 Apr 2018, 15:01

              @VRonin said in Need Help with getting a slot executed in the correct thread:

              Close-to-manadatory reading: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

              C Offline
              C Offline
              Curtwagner1984
              wrote on 17 Apr 2018, 15:28 last edited by
              #6

              @VRonin

              I read it, and maybe I missed something but I didn't see how to keep the worker alive after it finished running it's 'process' function.

              1 Reply Last reply
              0
              • V Offline
                V Offline
                VRonin
                wrote on 17 Apr 2018, 15:53 last edited by
                #7

                the default implementation of QThread::run starts an event loop so if you use the method described in that link everything will work out of the box

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                C 1 Reply Last reply 17 Apr 2018, 16:08
                3
                • V VRonin
                  17 Apr 2018, 15:53

                  the default implementation of QThread::run starts an event loop so if you use the method described in that link everything will work out of the box

                  C Offline
                  C Offline
                  Curtwagner1984
                  wrote on 17 Apr 2018, 16:08 last edited by
                  #8

                  @VRonin said in Need Help with getting a slot executed in the correct thread:

                  the default implementation of QThread::run starts an event loop so if you use the method described in that link everything will work out of the box

                  Indeed it does! Thanks!

                  1 Reply Last reply
                  1

                  2/8

                  17 Apr 2018, 13:57

                  topic:navigator.unread, 6
                  • Login

                  • Login or register to search.
                  2 out of 8
                  • First post
                    2/8
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved