QT 线程池 + TCP 小试(一)线程池的简单实现
*免分资源链接点击打开链接http://download.csdn.net/detail/goldenhawking/4492378
很久以前做过ACE + MFC/QT 的中轻量级线程池应用,大概就是利用线程池执行客户机上的运算需求,将结果返回。ACE是跨平台重量级的通信中间件,与常见的应用程序框架需要精心契合,才能不出问题。最近想到既然QT框架本身就已经具有各类功能,何不玩一玩呢,那就开搞!这个实验的代码可以从我的资源内下载。
第一步打算实现的模式,我们需要一个设置为CPU核心数的线程池,这个线程池可以异步接受N个数据生产者传入的数据,均衡的分配处理任务,处理后的数据返回给某1个或者几个消费者。有两种均衡方法。一种是生产者粒度的均衡。同一个生产者的各批数据FIFO顺序不被打破,这需要判断,当处理线程队列中还有该生产者的数据时,不改变当前处理线程。第二种是数据粒度的并行,某个生产者传来的数据被分配到不同的线程,不保证后到的数据后被处理(也可能先到的处理的慢,后到的快)。
这种异步队列机制如果在MFC、WinAPI中,需要手工使用 Mutex 同步队列,更可恶的是分配的数据对象的生存期非常微妙,一不小心就会出红叉叉。QT首先为我们提供了信号和槽的机制,且该机制原生支持跨线程。假设我们在16核心服务器上,则使用 15个 QThread对象管理15组工作线程(留一个给主界面)。但是,如果仔细看了QT的文档,就会发现QThread的信号事件循环默认是在创建者中(很多时候就是主线程!),所以,要想让槽在子线程运行,一般是派生一个QObject的类,并把对象MoveToThread到某个QThread管理的线程上去。这样,信号和槽就是全异步FIFO了。其次,QT提供了引用计数的QByteArray封装,这个东西在参数传递的时候,速度很快,很少出现memcpy,生存期也特别容易控制。虽然C++11里有 shared_ptr<T>,但是那个东西还是需要在一开始new 一个int8型的存储区,很讨厌。
说了这么多,上关键代码。
先是线程池的封装qghthreadengine.h
- #ifndef QGHTHREADENGINE_H
- #define QGHTHREADENGINE_H
- #include <QObject>
- #include <QThread>
- #include <QVector>
- #include <QList>
- #include <QMap>
- #include <QMutex>
- #include "qghthreadtaskitem.h"
- #include "qghthreadobject.h"
- //线程池引擎,帮助用户进行动态平衡
- class QGHThreadEngine : public QObject
- {
- Q_OBJECT
- public:
- QGHThreadEngine(QObject *parent,QGHThreadTaskItem * pTaskItem,int nThreads = 2,bool bFIFOKeep = true);
- ~QGHThreadEngine();
- protected:
- QVector<QThread *> m_ThreadPool;
- QVector<QGHThreadObject *> m_ThreadObjs;
- QGHThreadTaskItem * m_pThreadTaskItem;
- int m_nThreads;
- bool m_bFIFOKeep;
- private:
- //各个m_ThreadPool\m_ThreadObjs的任务数
- QMap<QObject *,qint32> m_map_Tasks;
- //m_bFIFOKeep == true 时,下面两个成员将保证非空闲的单个 data_source 将始终在单一线程处理
- //各个data_source 目前的处理线程
- QMap<QObject *,QObject *> m_map_busy_source_task;
- //各个data_source 目前的排队数目
- QMap<QObject *,int> m_map_busy_source_counter;
- public:
- void SetThreadTaskItem(QGHThreadTaskItem * pTaskItem);
- QList<qint32> CurrentLoad()
- {
- return m_map_Tasks.values();
- }
- public slots:
- void append_new(QObject * data_source, const QByteArray & data);
- //捕获QGHThreadObject::sig_process_finished, 以便管理data_source的 FIFO 顺序
- void on_sig_process_finished(QObject * data_source);
- signals:
- //************************************
- // Method: do_task
- // FullName: QGHThreadEngine::do_task
- // Access: public
- // Returns: void
- // Qualifier:
- // Parameter: QObject * 任务来源 (相同任务源的任务,在队列非空时会被安排到同一个线程处理,以确保对相同源的FIFO)
- // Parameter: QByteArray 任务体
- // Parameter: QObject * 处理任务的线程对象(QGHThreadObject)
- //************************************
- void do_task(QObject *, const QByteArray &,QObject *);
- };
- #endif // QGHTHREADENGINE_H
实现qghthreadengine.cpp:
- #include "qghthreadengine.h"
- #include <assert.h>
- QGHThreadEngine::QGHThreadEngine(QObject *parent,QGHThreadTaskItem * pTaskItem,int nThreads,bool bFIFOKeep)
- : QObject(parent),
- m_nThreads(nThreads),
- m_pThreadTaskItem(pTaskItem),
- m_bFIFOKeep(bFIFOKeep)
- {
- assert(nThreads>0 && nThreads<512 && pTaskItem!=NULL);
- //创建固定数目的线程
- for (int i=0;i<nThreads;i++)
- {
- QThread * pNewThread = new QThread(this);
- QGHThreadObject * pNewObject = new QGHThreadObject(0,pTaskItem);
- //记录下来
- m_ThreadPool.push_back(pNewThread);
- m_ThreadObjs.push_back(pNewObject);
- m_map_Tasks[pNewObject] = 0;
- pNewThread->start();
- //把QGHThreadObject的信号、曹处理搬移到子线程内
- pNewObject->moveToThread(pNewThread);
- //连接处理完成消息
- connect(pNewObject,SIGNAL(sig_process_finished(QObject *)),this,SLOT(on_sig_process_finished(QObject *)));
- //连接处理新任务消息
- connect(this,SIGNAL(do_task(QObject *, const QByteArray &,QObject *)),pNewObject,SLOT(process(QObject *, const QByteArray &,QObject *)));
- }
- }
- QGHThreadEngine::~QGHThreadEngine()
- {
- foreach(QGHThreadObject * obj,m_ThreadObjs)
- {
- disconnect(obj,SIGNAL(sig_process_finished(QObject *)),this,SLOT(on_sig_process_finished(QObject *)));
- obj->deleteLater();
- }
- foreach(QThread * th ,m_ThreadPool)
- {
- disconnect(this,SIGNAL(do_task(QObject *, QByteArray,QObject *)),th,SLOT(process(QObject *, QByteArray,QObject *)));
- th->exit(0);
- th->wait();
- }
- }
- //负载均衡添加任务,生产者的信号要挂接到这个槽上
- void QGHThreadEngine::append_new(QObject * data_source, const QByteArray & data)
- {
- QObject * pMinObj = 0;
- //对一批来自同一数据源的数据,使用同样的数据源处理,以免发生多线程扰乱FIFO对单个data_source的完整性
- if (m_map_busy_source_counter.find(data_source)!=m_map_busy_source_counter.end()&& m_bFIFOKeep==true)
- {
- m_map_busy_source_counter[data_source]++;
- pMinObj = m_map_busy_source_task[data_source];
- }
- else
- {
- qint32 nMinCost = 0x7fffffff;
- //寻找现在最空闲的一个线程
- for (QMap<QObject *,qint32>::iterator p = m_map_Tasks.begin();p!=m_map_Tasks.end();p++)
- {
- if (p.value()< nMinCost)
- {
- nMinCost = p.value();
- pMinObj = p.key();
- }
- }
- if (pMinObj)
- {
- m_map_busy_source_counter[data_source] = 1;
- m_map_busy_source_task[data_source] = pMinObj;
- }
- }
- if (pMinObj)
- {
- m_map_Tasks[pMinObj]++;
- emit do_task(data_source,data,pMinObj);
- }
- }
- void QGHThreadEngine::on_sig_process_finished(QObject * data_source)
- {
- if (m_map_Tasks.find(sender())!=m_map_Tasks.end())
- {
- m_map_Tasks[sender()]--;
- }
- if (m_map_busy_source_counter.find(data_source)!=m_map_busy_source_counter.end())
- {
- m_map_busy_source_counter[data_source]--;
- if (m_map_busy_source_counter[data_source]<=0)
- {
- m_map_busy_source_counter.remove(data_source);
- m_map_busy_source_task.remove(data_source);
- }
- }
- }
用于绑定的 qghthreadobject.h
- #ifndef QGHTHREADOBJECT_H
- #define QGHTHREADOBJECT_H
- #include <QObject>
- #include "qghthreadtaskitem.h"
- //用于在子线程内具体承担事件循环的类,用户无需重载
- class QGHThreadObject:public QObject
- {
- Q_OBJECT
- public:
- QGHThreadObject(QObject *parent,QGHThreadTaskItem * pThreadTaskItem);
- ~QGHThreadObject();
- public:
- void SetThreadTaskItem(QGHThreadTaskItem * pThreadTaskItem);
- public slots:
- //************************************
- // Method: process
- // FullName: QGHThreadObject::process
- // Access: public
- // Returns: void
- // Qualifier:
- // Parameter: QObject * 任务来源 (相同任务源的任务,在队列非空时会被安排到同一个线程处理,以确保对相同源的FIFO)
- // Parameter: QByteArray 任务体
- // Parameter: QObject * 处理任务的线程对象(QGHThreadObject)
- //************************************
- void process(QObject * data_source, const QByteArray &data,QObject * target);
- private:
- QGHThreadTaskItem * m_pThreadTaskItem;
- signals:
- //信号,表示一次处理已经完成。QGHThreadEngine捕获该信号,管理data_source的 FIFO 顺序
- void sig_process_finished(QObject * data_source);
- };
- #endif
相应实现qghthreadobject.cpp
- #include "qghthreadobject.h"
- #include <assert.h>
- QGHThreadObject::QGHThreadObject(QObject *parent,QGHThreadTaskItem * pThreadTaskItem)
- : QObject(parent),
- m_pThreadTaskItem(pThreadTaskItem)
- {
- assert(pThreadTaskItem!=NULL);
- }
- QGHThreadObject::~QGHThreadObject()
- {
- }
- void QGHThreadObject::process(QObject * data_source, const QByteArray &data,QObject * target)
- {
- if (target==this)
- {
- m_pThreadTaskItem->run(data_source,data);
- emit sig_process_finished(data_source);
- }
- }
- void QGHThreadObject::SetThreadTaskItem(QGHThreadTaskItem * pThreadTaskItem)
- {
- assert(pThreadTaskItem!=NULL);
- m_pThreadTaskItem = pThreadTaskItem;
- }
最后,是供用户重载的实际处理方法的纯虚基类qghthreadtaskitem.h
- #ifndef QGHTHREADTASKITEM_H
- #define QGHTHREADTASKITEM_H
- #include <QObject>
- //用户重载该类,实现自定义方法的线程池调用
- class QGHThreadTaskItem:public QObject
- {
- Q_OBJECT
- public:
- QGHThreadTaskItem(QObject *parent);
- ~QGHThreadTaskItem();
- public:
- virtual void run(QObject * task_source, const QByteArray & data_array) = 0;
- };
- #endif
下次,继续写如何实现一个TCP链路,让这个线程池活起来
http://blog.csdn.net/goldenhawking/article/details/7854413
QT 线程池 + TCP 小试(一)线程池的简单实现的更多相关文章
- QT 线程池 + TCP 小试(三)实现最终功能
*免分资源链接点击打开链接http://download.csdn.net/detail/goldenhawking/4492378 有了TCP.线程池,我们就可以把他们连接起来.使用最简单的 QMa ...
- 线程池 异步I/O线程 <第三篇>
在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一 ...
- 转载 线程池 异步I/O线程 <第三篇>
在学习异步之前先来说说异步的好处,例如对于不需要CPU参数的输入输出操作,可以将实际的处理步骤分为以下三步: 启动处理: 实际的处理,此时不需要CPU参数: 任务完成后的处理: 以上步骤如果仅仅使用一 ...
- Android线程管理之ThreadPoolExecutor自定义线程池
前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...
- 【转】线程及同步的性能 - 线程池 / ThreadPoolExecutors / ForkJoinPool
线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...
- java多线程系类:JUC线程池:05之线程池原理(四)(转)
概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- java多线程系类:JUC线程池:02之线程池原理(一)
在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...
- java多线程系类:JUC线程池:01之线程池架构
概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...
随机推荐
- 简单的Mvp设计
任务:从网络上获取数据,然后显示在MainActivity的ListView上 一.载入需要用的框架 1.Mvp框架 compile 'com.hannesdorfmann.mosby:mvp:2.0 ...
- 1206: B.求和
题目描述 点击这里 对于正整数n,k,我们定义这样一个函数f,它满足如下规律 现在给出n和k,你的任务就是要计算f(n,k)的值. 输入 首先是一个整数T,表示有T组数据 接下来每组数据是n和k(1& ...
- UVA 1599 Ideal Path (HDU 3760)
两次bfs: 第一次bfs逆向搜索,得到每个点到终点的最短距离,找出最短路:第二次bfs根据最短距离可以选择满足条件的最短路. 注意!碰到这种很大数据量的题目一定要记得用scanf,printf 输入 ...
- Sql Server专题一:索引(下)
首先这次的内容是全文索引,跟前面讲的其实没有多大关系 两种索引的功能和结构都是不同的,普通索引的结构主要以B+树和哈希索引为主,用于实现对字段中数据的精确查找,比如查找某个字段值等于给定值的记录,A= ...
- SQL Server数据库空间管理 (2)
本篇内容主要解决剩余的两个问题:2).日志文件不停增长 4).自动增长和自动收缩 日志文件不停增长的解决 首先,当日志文件超过预期的时候,我们然要看看日志文件中存放了什么内容:DBCC LOG ; ...
- Jquery一个slideToggle搞定div的隐藏与显示
Jquery一个slideToggle搞定div的隐藏与显示 <!DOCTYPE html> <html> <head> <script src=" ...
- 把自定义控件集成到Qt Designer中
要想在Qt Designer中使用自定义控件,必须要使Qt Designer能够知道我们的自定义控件的存在.有两种方法可以把新自定义控件的信息通知给Qt Designer:“升级(promotion) ...
- (十)boost库之多线程
(十)boost库之多线程 1.创建线程 使用boost库可以方便的创建一个线程,并提供最多支持9个参数的线程函数,相对于void*来说,方便了很多,创建线程主要提供了一下3种方式: 线程库头文件:# ...
- UML_交互图
交互图(Interaction Diagram)用来描述系统中的对象是如何进行相互作用的.即一组对象是如何进行消息传递的. 当交互图建模时,通常既包括对象(每个对象都扮演某一特定的角色),又包括消息( ...
- C Statements
1,while((ch = getchar()) != EOF){ putchar(ch);}2,while((ch=getchar()) != EOF){ if(ch < '0' ...