0

I am trying to modify the Qt Fortune Threaded Server example to read text from the connection and then echo it back. I defined tcpSocket in my FortuneThread.h file as follows:

QTcpSocket tcpSocket;

My new run function for the thread looks as follows:

void FortuneThread::run()
{
    if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
        emit error(tcpSocket.error());
        return;
    }
    connect(&tcpSocket, SIGNAL(readyREAD()), this, SLOT(readCommand()) );
}

Which compiles and runs, but once I connect I get this error (referring to the connect line):

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x1eeb920), parent's thread is QThread(0x1bb3f90), current thread is FortuneThread(0x1eeb8f0)
QObject::connect: No such signal QTcpSocket::readyREAD() in ../fortune/fortunethread.cpp:60

Can someone explain the cause to me? Since tcpSocket is defined inside the FortuneThread class (which is run as a separate thread), and "this" refers to FortuneThread, I assume both objects are inside the thread? How to fix this?

László Papp
  • 51,870
  • 39
  • 111
  • 135
TSG
  • 4,242
  • 9
  • 61
  • 121

3 Answers3

4

Your socket object has been created in the main thread, but you're accessing it from a different thread. You need to create it inside of the thread's run() method. The location where the socket is defined doesn't matter. It will be created from the main thread when the C++ runtime library is doing static object initialization.

QTcpSocket * tcpSocket;

...

void FortuneThread::run() {
  tcpSocket = new QTcpSocket;
  ...
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • OK this made sense and the crash is gone. However, the readCommand function (slot) is never called when I send text through my telnet connection. Anything else obvious? If not I will start a new topic. Thanks – TSG Sep 27 '13 at 16:47
  • @Michelle: There isn't enough code here to see where the problem is. You'd need to create a single-file, self-contained example that opens both the server and the client connection (to itself). This may prove enough for you to understand where the problem is. – Kuba hasn't forgotten Monica Sep 27 '13 at 17:45
2

I agree with Kuba Ober. You should read that great guide about Qt threads, objects and events. In particular, section called Signals and slots across threads. Authors recommends split controller and work parts into different essences.

Second issue in your code — case sensitive signal name. Change it to readyRead.

Community
  • 1
  • 1
Eugene Mamin
  • 749
  • 4
  • 8
1

A problem with the Qt Fortune Threaded Server example is the way in which it uses threads. As the developers of Qt say, "You're doing it wrong"

The issue is the inheritance of QThread. The QThread class is not actually a thread, but a thread controller class and the only reason to inherit this, is if you want to change the behaviour of controlling threads.

The problem you're seeing is due to thread affinity; which thread an object belongs to.

If a thread is inherited like this: -

class FortuneThread : public QThread
{
    Q_OBJECT

    private:
        QTcpSocket tcpSocket;
};

An object of FortuneThread is then instantiated from the main thread: -

FortuneThread* ft = new FortuneThread(parent);

The thread affinity for the thread and the objects it has instantiated (tcpSocket) is now the main thread, so the tcpSocket is running on the main thread, which is what the error is stating. At the point the run function is called, the connect is coming from the FortuneThread, but the tcpSocket is on the main thread.

The better way to solve this is to create your class, derived from QObject and move it to the thread: -

// Inherit from QObject, not QThread
class FortuneSocket : public QObject
{
    Q_OBJECT

    public slots:
       void Run();
    private:
        QTcpSocket tcpSocket;
};


QThread* pThread = new QThread(parent);
FortuneSocket* pFortune = new FortuneSocket(parent);

connect(pThread, &QThread::started, pFortune, &FortuneSocket::Run); // Qt5 connect style

// move the fortune socket to the thread: -
pFortune->moveToThread(pThread);

Now, when you start the thread with pThread->start(), the FortuneSocket object and all of its members are running on the new thread.

Using threads this way also means that you can move multiple objects to a single thread, rather than having one object per thread. Note that creating more threads than CPU cores is pointless!

Finally, there's a more in-depth article on how to use QThread, here.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85