大佬教程收集整理的这篇文章主要介绍了66.QT-线程并发、QTcpServer并发、QThreadPool线程池,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
1.线程并发@H_607_3@一个程序内部能拥有多个线程并行执行。一个线程的执行可以被认为是一个CPU在执行该程序。当一个程序运行在多线程下,就好像有多个CPU在同时执行该程序。总之,多线程即可以这么理解:多线程是处理高并发的一种编程方法,即并发需要用多线程实现。
2.如何分配线程数量@H_607_3@利用 CPU 核心数,应用并发编程来提高效率.线程IO时间所占比例越高,需要越多线程;线程CPU时间所占比例越高,需要越少线程。理论上:
线程数量 = CPU 核数(逻辑)+ 1
为什么+1,《Java并发编程实战》这么说:
@H_450_34@IO时间和CPU时间@H_607_3@
@H_450_34@所以对于单核CPU而言:
最佳线程数 = 1 + (IO操作耗时/CPU操作耗时)
比如: IO操作耗时为1500ms、CPU操作耗时为500ms
最佳线程数 = 1 + (IO操作耗时/CPU操作耗时) = 1 + (1500/500) = 4
对于多核CPU而言:
最佳线程数 = CPU核心数 * (1 + (IO操作耗时/CPU操作耗时))
3.QTcpServer并发@H_607_3@QTcpServer要实现并发,首先需要子类化QTcpServer,然后重写incomingConnection()函数.该函数定义如下所示:
[virtual protected] void QTcpServer::incomingConnection(qintptr socketDescriptor) // 当有新连接时,首先会调用该函数,通过socketDescriptor参数(连接本机的套接字)创建一个QTcpSocket,设置套接字描述符,然后将QTcpSocket存储在一个内部挂起连接列表中。最后触发newConnection()。
我们重写该函数,通过一个QThread将socketDescriptor参数传到一个线程中,然后调用socketDescriptor()函数初始化一个QTcpSocket.从而达到QThread中生成一个新的QTcpSocket.
@H_582_5@myServer重写如下所示:@H_607_3@
void MyServer::incomingConnection(qintptr socketDescriptor) { myThread *thread = new myThread(socketDescriptor, this); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->start(); }
void myThread::run() { QTcpSocket tcpSocket; // 初始化一个QTcpSocket if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { emit error(tcpSocket.error()); return; } // 发送字符串 tcpSocket.write("123456".toLocal8Bit()); tcpSocket.disconnectFromHost(); tcpSocket.waitForDisconnected(); }
然后在widget中:@H_607_3@
server.listen(QHostAddress::AnyIPv4,8080);
每当一个client连接该server时,就会接收到"123456"@H_607_3@,然后被断开.
4.线程池概念@H_607_3@假如服务器突然来了500个任务,但是我们最佳线程数是20个,不可能立马创建500个线程,因为线程过多会带来调度开销,进而影响缓存局部性和整体性能。所以我们需要线程池,线程池不仅能够保证内核的充分利用,还能防止过分调度。 线程池就相当于排队去银行办理业务.排队的人就是要处理业务的任务线程,客服就是线程池中容纳办理业务的最大数量.每当一个办理业务的线程结束后,线程池就会从等待队列中取出一个线程进行业务办理.
5.QThreadPool并发线程池@H_607_3@在Qt中,线程池可以使用QThreadPool类@H_607_3@,用来管理多个QThread的集合.QThreadPool管理和回收单独的QThread对象,以帮助减少使用线程的程序中创建线程的成本。每个Qt应用程序都有一个全局QThreadPool对象,可以通过调用globalInstance()来访问(也可以自己定义个QThreadPool)@H_607_3@。要使用一个QThreadPool线程,需要子类化QRunnable@H_607_3@.并实现run()虚函数。然后创建一个子类化QRunnable类的一个对象,并将其传递给QThreadPool::start(),来启动一个线程.start()函数如下所示:
void QThreadPool::start(QRunnable *runnable, int priority = 0) // 启动一个runnable,如果当前线程池数量超过了maxThReadCount(),那么将runnable添加到等待队列中. // priority参数可用于控制runnable在等待队列中的被执行的顺序。 // 默认runnable->auto@R_674_4453@返回true,线程池将获得可运行对象的所有权,并且在runnable->run()返回后,可运行对象将被线程池自动删除。 // 可以通过QRunnable::setAuto@R_674_4453@来更改自动删除标志
QThreadPool支持通过在QRunnable::run()中调用tryStart(this)来多次执行同一个QRunnable。如果autodelete被启用,QRunnable将在最后一个线程退出run函数时被删除。当autodelete启用时,使用相同的QRunnable多次调用start()会创建一个竞争条件,不建议这样做。
在一定时间内@R_489_10780@线程将过期。默认超时时间为30000毫秒(30秒)。这可以使用setExpiryTimeout(int)来更改。设置负数将禁用过期机制。
调用maxThReadCount()查询要使用的最大线程数。也可以使用setMaxThReadCount()来更改这个限制。默认值是QThread::idealThReadCount().@H_607_3@ 该函数定义如下所示:
[static] int QThread::idealThReadCount() //返回系统上可以运行的理想线程数。这是通过查询系统中真实的和逻辑的处理器核的数量来完成的。 //如果无法检测到处理器核数,则该函数返回1。
示例如下所示:
#include <QCoreApplication> #include <QThread> #include <QDebug> #include <QRunnable> #include <QThreadPool> class ComputeTask : public QRunnable { int index; void run() override { const int work = 1000 * 1000 * 40; // 每个任务计数40000000次 volatile int v = 0; for (int j = 0; j < work; ++j) ++v; qDebug() << index << " thread: " << QThread::currentThreadId(); } public: ComputeTask(int i) { index = i; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); const int cnt = 200; // 200个任务 QThreadPool pool; qDebug() << "@H_131_41@maxThReadCount: " << pool.maxThReadCount(); for (int i = 0; i < cnt; ++i) { ComputeTask *compute = new ComputeTask(i); pool.start(computE); } return a.exec(); }
打印如下所示:
以上是大佬教程为你收集整理的66.QT-线程并发、QTcpServer并发、QThreadPool线程池全部内容,希望文章能够帮你解决66.QT-线程并发、QTcpServer并发、QThreadPool线程池所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。