Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I execute QTcpSocket in a different thread?

How do I execute QTcpSocket functions in a different thread?

like image 699
Regof Avatar asked Apr 23 '10 11:04

Regof


4 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 each QTcpSocket, 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.

like image 199
Nicolas Holthaus Avatar answered Sep 29 '22 08:09

Nicolas Holthaus


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 the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.

like image 27
Michael Avatar answered Sep 29 '22 07:09

Michael


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

like image 23
MSalters Avatar answered Sep 29 '22 08:09

MSalters


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.

like image 20
Luca Carlon Avatar answered Sep 29 '22 08:09

Luca Carlon