Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do QObject::moveToThread() when using QThreadPool?

I'm building a small multithreaded web server. The QTcpSockets are fetched in the main thread and then hand over by QtConcurrent to the QThreadPool, which eventually processes the data and sends out an answer.

My problem is that the socket is created in the main thread and processed in another one. This causes errors when trying to write to the socket:

socket->write(somedata);

QObject: Cannot create children for a parent that is in a different thread. (Parent is QNativeSocketEngine(0x608330), parent's thread is QThread(0x600630), current thread is QThread(0x505f60)

The clean way would be to move the socket object to the processing thread using

socket->moveToThread(QThread::currentThread()).

This, however, can only be called within the thread the object was created in. Furthermore, the socket has the QTcpServer object as parent, so moveToThread() will fail anyway (parented objects cannot switch threads).

How can I move the object to the QThread::currentThread() within the code that is run by the threadpool? Alternatively, how can I write to a socket outside the thread it was created?

like image 339
grefab Avatar asked Jun 27 '10 02:06

grefab


People also ask

How do you use QThreadPool?

Each Qt application has one global QThreadPool object, which can be accessed by calling globalInstance(). To use one of the QThreadPool threads, subclass QRunnable and implement the run() virtual function. Then create an object of that class and pass it to QThreadPool::start().

How do you create a thread in Qt?

We make the MyThread class get inherited from QThread. In the code, we creates three instances of MyThread class with QString names ("A", "B", and "C"). The void QThread::start(Priority priority = InheritPriority) slot begins execution of the thread by calling run() which we overrides in MyThread class.


2 Answers

Extend QTcpServer, reimplement incomingConnection(int socketDescriptor) and pass the socket descriptor to the threads in the pool. Have the QRunnable create the QTcpSocket from the descriptor and start an event loop to receive that socket's signals.

like image 143
andref Avatar answered Nov 20 '22 09:11

andref


I would also agree that catching incomingConnection(int) and creating the socket in the worker thread is the way to go here.

But your problem was a more fundamental one, so allow me to show an alternative that solves the general problem (moving arbitrary QObjects to threads in a thread pool):

  1. In the main thread: dissociate the socket from any thread:

    socket->moveToThread(nullptr);

    This will suspend event processing of the socket. Then pass it to QtConcurrent.

  2. In the function executed on the thread pool, you can now call

    socket->moveToThread(QThread::currentThread());

    This will re-start event processing.

With any luck, the socket will have its socket notifiers re-registered with the worker thread's event loop again, and you can serve the connection from the worker.

There are two important caveats here, though:

  • You should not put blocking tasks on the global thread pool (QThreadPool::globalInstance()). As the name of the function suggests, that thread pool instance is a global resource, shared by all users of QtConcurrent, and blocking its worker threads in the best case reduces the capacity (thus, throughput) of the pool and in the worst case will dead-lock.
  • If you use the event loop of worker threads (to not use the synchronous API of QTcpSocket, because of the first point), you'd need to call QThread::currentThread()->exec() to start it. That, however, is a blocking call and to the thread pool scheduler, the worker will look busy, so it will not hand it more tasks (ie. QRunnables).

If you actually want to implement a multi-threaded tcp server, you'll therefore need to roll your own thread pool.

like image 26
Marc Mutz - mmutz Avatar answered Nov 20 '22 10:11

Marc Mutz - mmutz