Qt5 Tutorial QTcpServer with QThreadPool - 2020
In this tutorial, we will learn how to setup Multithreaded Client and Server using QThreadPool.
A thread pool is a collection of runnables with a work queue. The threads in the pool constantly run and check the task queue for a new task.
Even though the threads are light-weighted than creating a process, creating and destroying them consumes resources. Thread Pool is preferred over creating a new thread for each task when there is a large number of short tasks to be done rather than a small number of long ones. This prevents having to incur the overhead of creating a thread a large number of times. So, creating a ThreadPool is a better solution since a finite number of threads can be pooled and reused. The runnable or callable tasks will be placed in a queue, and the finite number of threads in the pool will take turns to work on the tasks in the queue.
From Qt5 document
The QThreadPool class manages a collection of QThreads.
QThreadPool manages and recyles individual QThread objects to help reduce thread creation costs in programs that use threads. 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().
class HelloWorldTask : public QRunnable { void run() { qDebug() << "Hello world from thread" << QThread::currentThread(); } } HelloWorldTask *hello = new HelloWorldTask(); // QThreadPool takes ownership and deletes 'hello' automatically QThreadPool::globalInstance()->start(hello);QThreadPool deletes the QRunnable automatically by default. Use QRunnable::setAutoDelete() to change the auto-deletion flag.
QThreadPool supports executing the same QRunnable more than once by calling tryStart(this) from within QRunnable::run(). If autoDelete is enabled the QRunnable will be deleted when the last thread exits the run function. Calling start() multiple times with the same QRunnable when autoDelete is enabled creates a race condition and is not recommended.
Threads that are unused for a certain amount of time will expire. The default expiry timeout is 30000 milliseconds (30 seconds). This can be changed using setExpiryTimeout(). Setting a negative expiry timeout disables the expiry mechanism. Call maxThreadCount() to query the maximum number of threads to be used. If needed, you can change the limit with setMaxThreadCount(). The default maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount() function returns the number of threads currently doing work.
The reserveThread() function reserves a thread for external use. Use releaseThread() when your are done with the thread, so that it may be reused. Essentially, these functions temporarily increase or reduce the active thread count and are useful when implementing time-consuming operations that are not visible to the QThreadPool.
Note that QThreadPool is a low-level class for managing threads, see the Qt Concurrent module for higher level alternatives.
For TCP Socket, please visit my C++ Tutorials: Socket - Server and Client.
For Multithreading, please visit my C++ Tutorials: Multithreading in C++.
We'll start with Qt Console Application.
First, we need to add network module to our project file, MultiThreadedQTcpServer.pro:
QT += core QT += network QT -= gui TARGET = QTcpServerThreadPool CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp
Then, we want to create a new class called MyServer.
Another class called MyRunnable should be created as well.
Here is the brief summary. The code will be presented shortly.
- In main(), we create a server and start it:
MyServer server; server.startServer();
- Then, in the constructor, MyServer::MyServer(), an object of QThreadPool is created with max thread count = 5:
pool = new QThreadPool(this); pool->setMaxThreadCount(5);
- Then, the server starts listening to any incoming connection request from a client:
void MyServer::startServer() { if(this->listen(QHostAddress::Any, 1234)) ...
- QTcpServer gets a new connection request from a client:
MyServer::incomingConnection(qintptr handle)
where the handle is actually the native socket descriptor for the accepted connection. - It makes a task (runnable) object in MyServer::incomingConnection():
MyRunnable *task = new MyRunnable();
- The QRunnable class is an interface for representing a task or piece of code that needs to be executed.
- What's in the task?
We reimplement the MyRunnable::run() function to represent (or set) the task.
void MyRunnable::run() { if(!socketDescriptor) return; QTcpSocket socket; socket.setSocketDescriptor(socketDescriptor); socket.write("From server: hello world"); socket.flush(); socket.waitForBytesWritten(); socket.close(); }
- Then, the server grabs one of the threads
and throws the runnable to the thread.
pool->start(task);
- After task is done, it will be automatically deleted because we set it already:
task->setAutoDelete(true);
2. Client is about to request connection using telnet:
3. Client requested a connection and got a response from the server:
4. At a new incoming connection request, the server grabs a thread from the QThreadPool:
5. At another new incoming connection request, the server grabs another thread from the QThreadPool:
Here is the source file: QTcpServerThreadPool.zip
main.cpp:
#include <QCoreApplication> #include "myserver.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyServer Server; Server.startServer(); return a.exec(); }
myserver.h:
// MyServer.h #ifndef MYSERVER_H #define MYSERVER_H #include <QTcpServer> #include <QThreadPool> #include <QDebug> class MyServer : public QTcpServer { Q_OBJECT public: explicit MyServer(QObject *parent = 0); void startServer(); protected: void incomingConnection( qintptr handle ); signals: public slots: private: QThreadPool *pool; }; #endif // MYSERVER_H
myserver.cpp:
// MyServer.cpp #include "myserver.h" #include "myrunnable.h" MyServer::MyServer(QObject *parent) : QTcpServer(parent) { pool = new QThreadPool(this); pool->setMaxThreadCount(5); } void MyServer::startServer() { if(this->listen(QHostAddress::Any, 1234)) { qDebug() << "Server started"; } else { qDebug() << "Server did not start!"; } } void MyServer::incomingConnection(qintptr handle) { // 1. QTcpServer gets a new connection request from a client. // 2. It makes a task (runnable) here. // 3. Then, the server grabs one of the threads. // 4. The server throws the runnable to the thread. // Note: Rannable is a task not a thread MyRunnable *task = new MyRunnable(); task->setAutoDelete(true); task->socketDescriptor = handle; pool->start(task); qDebug() << "pool started"; }
myrunnable.h:
// myrunnable.h #ifndef MYRUNNABLE_H #define MYRUNNABLE_H #include#include #include class MyRunnable : public QRunnable { public: MyRunnable(); protected: void run(); public: qintptr socketDescriptor; }; #endif // MYRUNNABLE_H
myrunnable.cpp:
// MyRunnalbe.cpp #include "myrunnable.h" MyRunnable::MyRunnable() { } void MyRunnable::run() { if(!socketDescriptor) return; QTcpSocket socket; socket.setSocketDescriptor(socketDescriptor); socket.write("From server: hello world"); socket.flush(); socket.waitForBytesWritten(); socket.close(); }
Qt 5 Tutorial
- Hello World
- Signals and Slots
- Q_OBJECT Macro
- MainWindow and Action
- MainWindow and ImageViewer using Designer A
- MainWindow and ImageViewer using Designer B
- Layouts
- Layouts without Designer
- Grid Layouts
- Splitter
- QDir
- QFile (Basic)
- Resource Files (.qrc)
- QComboBox
- QListWidget
- QTreeWidget
- QAction and Icon Resources
- QStatusBar
- QMessageBox
- QTimer
- QList
- QListIterator
- QMutableListIterator
- QLinkedList
- QMap
- QHash
- QStringList
- QTextStream
- QMimeType and QMimeDatabase
- QFile (Serialization I)
- QFile (Serialization II - Class)
- Tool Tips in HTML Style and with Resource Images
- QPainter
- QBrush and QRect
- QPainterPath and QPolygon
- QPen and Cap Style
- QBrush and QGradient
- QPainter and Transformations
- QGraphicsView and QGraphicsScene
- Customizing Items by inheriting QGraphicsItem
- QGraphicsView Animation
- FFmpeg Converter using QProcess
- QProgress Dialog - Modal and Modeless
- QVariant and QMetaType
- QtXML - Writing to a file
- QtXML - QtXML DOM Reading
- QThreads - Introduction
- QThreads - Creating Threads
- Creating QThreads using QtConcurrent
- QThreads - Priority
- QThreads - QMutex
- QThreads - GuiThread
- QtConcurrent QProgressDialog with QFutureWatcher
- QSemaphores - Producer and Consumer
- QThreads - wait()
- MVC - ModelView with QListView and QStringListModel
- MVC - ModelView with QTreeView and QDirModel
- MVC - ModelView with QTreeView and QFileSystemModel
- MVC - ModelView with QTableView and QItemDelegate
- QHttp - Downloading Files
- QNetworkAccessManager and QNetworkRequest - Downloading Files
- Qt's Network Download Example - Reconstructed
- QNetworkAccessManager - Downloading Files with UI and QProgressDialog
- QUdpSocket
- QTcpSocket
- QTcpSocket with Signals and Slots
- QTcpServer - Client and Server
- QTcpServer - Loopback Dialog
- QTcpServer - Client and Server using MultiThreading
- QTcpServer - Client and Server using QThreadPool
- Asynchronous QTcpServer - Client and Server using QThreadPool
- Qt Quick2 QML Animation - A
- Qt Quick2 QML Animation - B
- Short note on Ubuntu Install
- OpenGL with QT5
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part A
- Qt5 Webkit : Web Browser with QtCreator using QWebView Part B
- Video Player with HTML5 QWebView and FFmpeg Converter
- Qt5 Add-in and Visual Studio 2012
- Qt5.3 Installation on Ubuntu 14.04
- Qt5.5 Installation on Ubuntu 14.04
- Short note on deploying to Windows
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization