How do I execute QTcpSocket functions in a different thread?
-
**It's not possible**. I have learned this hard way. See [my question](https://stackoverflow.com/q/60019664/514235). Have raised a bug-suggestion to Qt to `moveToThread () = delete` for these sockets and server classes. [QTBUG-82373](https://bugreports.qt.io/browse/QTBUG-82373) – iammilind Feb 21 '20 at 03:11
5 Answers
It's important to note what you can and can't do in terms of threading QTcpSocket
:
you can use it in a non-main thread, but only the thread in which it was created in.
you cannot call different functions on the
QTcpSocket
from different threads, e.g. read in one thread, write in the other. Instead, you can make a seperate thread for eachQTcpSocket
, which keeps them from using up time and resources that could be painting your widgets in the main thread.
IMO, putting your IO, including QTcpSocket
in a thread other than the main thread is a best practice and must-do for any performant application. I use QTcpSocket
in non-main threads all the time using the following idiom:
// Read data from a QTcpSocket in a thread. Assumes this is in some class.
m_thread = std::thread([this]
{
QEventLoop eventLoop;
QTcpSocket* socket = new QTcpSocket(&eventLoop);
socket->connectToHost("localhost", 9999);
// enqueue or process the data
QObject::connect(socket, &QTcpSocket::readyRead, &eventLoop, [socket]
{
m_concurrentQueue.push_back(socket->readAll());
});
// Quit the loop (and thread) if the socket it disconnected. You could also try
// reconnecting
QObject::connect(socket, &QTcpSocket::disconnected, &eventLoop, [&eventLoop]
{
eventLoop.quit();
});
eventLoop.exec();
delete socket;
});
where m_thread
is some member thread (basically just ensuring that it has a lifetime greater than the current immediate scope), and m_concurrentQueue
is some thread-safe queue, or std
container with mutex protection.
You'll also want to connect some signal (I usually call it joinAll
) to the event loop quit function, and call it from the class destructor. When using an event-loop-in-a-thread idiom you always have to be careful about making sure you can actually destroy the class correctly, otherwise your program won't exit (or on windows it will be terminated, usually with some destructors not getting called, and it ends up being a silent error).
I also usually use a condition variable to wait after creating the thread until the event loop has started. It's not necessary but if you are putting these threads together in constructors it can help make the program flow make more sense.

- 7,763
- 4
- 42
- 97
-
Thanks for your answer. I already upvoted on the day you posted. I had found this fact hard way that the `QTcpSocket` or `QWebSocket` are not thread-movable! Have raised a bug to Qt on this regard and put it under the question above. – iammilind Feb 21 '20 at 03:08
-
@Nicolas, this solution looks great but I wounder how to handle data of `m_concurrentQueue` in main thread, I mean should I use `QTimer` or just signal to notify main thread to start handle queues data? Which variant is the best in terms of performance? Thanks. – ebeglary May 11 '20 at 06:50
-
Is there a reason you use a lambda for eventLoop.quit instead of `&QEventLoop::quit`? – cdgraham Sep 11 '20 at 19:54
-
@cdgraham I don't think so, probably copied it from something that did some cleanup on exit. – Nicolas Holthaus Sep 11 '20 at 20:00
The QT docs are explicit that the QTCPSocket
should not be used accross threads. I.E, create a QTCPSocket
in the main thread and have the signal tied to an object in another thread.
I suspect that you are implementing something like a web server where the listen creates a QTCPSocket
on the accept. You then want another thread to handle the task of processing that socket. You can't.
The way I worked around it is I kept the socket in the thread it was born in. I serviced all of the incoming data in that thread and threw it into a queue where another thread could work on that data.
virtual void incomingConnection(qintptr socketDescriptor)
Note: If another socket is created in the reimplementation of this method, it needs to be added to the Pending Connections mechanism by calling
addPendingConnection()
.Note: If you want to handle an incoming connection as a new
QTcpSocket
object in another thread you have to pass thesocketDescriptor
to the other thread and create theQTcpSocket
object there and use itssetSocketDescriptor()
method.
-
1
-
That note was added by the community, and in my emperical experience is not correct. Nor does it seem you can simulate the behavior by passing socket descriptors as they claim, since you end up with two `QTcpSocket` instances (the implicit child of the server and the instance you create in the new thread) running on the same descriptor, which causes run-time assertion failures. – Nicolas Holthaus Jan 14 '15 at 12:30
-
You say you "serviced all of the incoming data in that thread and threw it into a queue where another thread could work on that data." How do you do this? – Flare Cat Aug 28 '16 at 02:46
-
@NicolasHolthaus, added a note backing up the answer. I am also facing a crash where I move a `QTcpSocket` created in a main thread to another worker thread. Upon checking the stack-trace, it appears that the main thread still retains some object of type `QSslSocketBackendPrivate` which writes the data. This causes the crash. In past I was misunderstanding this situation and was blaming the issue on my coding error. But it seems there is something wrong with the Qt library. See [this](https://stackoverflow.com/q/60019664/514235). What is the correct way to move a TCP socket to another thread? – iammilind Feb 12 '20 at 15:50
-
@iammilind it's best not to move them, but to *create* them in a different thread. I added an answer showing the idiom I use to do it. – Nicolas Holthaus Feb 12 '20 at 16:29
What I read in the docs is that QTcpSocket should not be used across threads. If you want to use it in another thread Qt docs say you should create a new QTcpSocket instance in your thread and set the descriptor on the new instance. To do this you need to reimplement QTcpServer and use QTcpServer::incomingConnection. A simple example is provided here.

- 9,546
- 13
- 59
- 91
Put a QMutex
lock around all calls, not just on the "different" thread but on all threads. One easy way to do so is via a QMutexLocker

- 173,980
- 10
- 155
- 350
-
["**Note:** All functions in this class are reentrant"](http://qt-project.org/doc/qt-4.7/qtcpsocket.html). If you have a more authoritative source for your contradicting claim, please include that. – MSalters Jan 14 '15 at 12:45
-
You're right. However, (and what I meant to say was) the class is not thread-safe, and the re-entrancy only guarantees you can use the class from multiple threads if you have different instances, but different `QTcpSocket` instances cannot share socket descriptors. Additionally, a single instance of a `QTcpSocket` cannot be accessed from multiple threads, regardless of whether it is mutexed. – Nicolas Holthaus Jan 14 '15 at 12:53
QTcpSocket
and QUdpSocket
cannot be moved to a different thread once they have been "opened". This is not supported by Qt and (mostly) doesn't work.
While they can be moved when they are in the "closed" state, that's not generally particularly useful.
What you should do is either:
Create the socket inside the thread it will be used in and use signals to marshal the data to be sent/received between threads.
Pass a
socketDescriptor
from the thread that starts opening the connection across to the thread where the socket should live, and open the socket there using thesocketDescriptor
.
Common gotchas:
When using QTcpServer
, it will open QTcpSockets in the same thread as itself. As they're already open, they cannot be moved.
If you want QTcpServer
to open incoming sockets in a different worker thread to the server itself (very common!), you must subclass it and override QTcpServer::incomingConnection()
to get access to the socket descriptor before it creates a QTcpSocket
. You cannot use the default implementation.
If you are subclassing QThread
, do not create the sockets in the constructor. They must be created in QThread::run()

- 673
- 1
- 9
- 16