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?
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().
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.
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.
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 QObject
s to threads in a thread pool):
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.
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:
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.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. QRunnable
s).If you actually want to implement a multi-threaded tcp server, you'll therefore need to roll your own thread pool.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With