理解多线程管理类 CWorkQueue
有些人会觉得多线程无非是,有多少任务就启动多少线程,CreadThread,执行完了自己结束就释放资源了,其实不然。多线程是需要管理的,线程的启动、执行、等待和结束都需要管理,线程间如何通信,如何共享内存数据,如果保证线程间的同步,避免死锁,都要考虑。
以前做项目时,用过 Codeproject 上一个线程管理的代码 Work Queue[1],很好用,也是不错的学习资料,但对于多线程初学者也不是一眼就能看懂的,所以今天打算对这个代码做个解读笔记,可为其它学习者提供一个参考,也深化自己对多线程的理解。关于该类的使用可直接访问原网址。
这个多线程管理类为 CWorkQueue,使用的是生产者-消费者模式。CWorkQueue 创建的每个线程都是一个消费者,生产者是类成员 m_pWorkItemQueue。生产者资源由外界使用者通过 InsertWorkItem 成员函数注入,然后通过 ReleaseSemaphore 通知消费者(即线程)处理,消费者线程 ThreadFunc 自创建起始就一直在等待,等待生产者通知,接到有任务通知后,线程就执行任务,执行完毕后继续等待。
主线程通过两种机制来跟已建立的新线程通信,信号量 Semaphore 和 事件 Event,Semaphore 用于通知新线程执行任务,Event 用于通知新线程结果自己。
1、信号量 Semaphore
由上可知,生产者和消费者使用的通信机制是信号量 Semaphore。信号量 Semaphore 只有两种状态,触发和未触发,决定状态的是当前资源数量,数量大于0表示信号量处于触发,等于0表示资源已经耗尽故信号量处于末触发。影响当前资源数量的函数有几个,下面依次介绍。
首先 CWorkQueue 在 Create 函数中创建了 Semaphore 信号量,并存在成员变量 m_phSincObjectsArray[SEMAPHORE_INDEX] 中,信号量的初始计数为 0,最大计数为 2147483647L。
m_phSincObjectsArray[SEMAPHORE_INDEX] = CreateSemaphore(NULL,,LONG_MAX,NULL); //创建Semaphore对象
每当用户调用 InsertWorkItem 将工作任务插入到队列(m_pWorkItemQueue->push(pWorkItem);)后,都要调用 ReleaseSemaphore,这会增加信号量的当前资源计数,该程序里试加 1。
if (!ReleaseSemaphore(m_phSincObjectsArray[SEMAPHORE_INDEX],,NULL))
{
assert(false);
return false;
}
在 CWorkQueue 在 Create 函数中还同时创建了线程,这些线程 ThreadFunc 自运行时就处于 WaitForMultipleObjects 状态。等待函数会检查信号量的当前资源计数,如果大于0(即信号量处于触发状态),减1后返回让调用线程继续执行。一个线程可以多次调用等待函数来减小信号量。线程中设置了无限循环 for(;;),因此当线程执行完当前处理后会继续等待信号量。
//等待两个事件
dwWaitResult = WaitForMultipleObjects(NUMBER_OF_SYNC_OBJ,pWorkQueue->m_phSincObjectsArray,FALSE,INFINITE);
2、事件 Event
在 Create 函数中创建信号量 Semaphore 的同时,还创建了事件, 并存在成员变量中,主要用于通知各线程结束执行。
m_phSincObjectsArray[ABORT_EVENT_INDEX] = CreateEvent(NULL,TRUE,FALSE,NULL); //创建event 事件对象,初始化时为无信号状态,使用手动重置为无信号状态
当用户调用 Destroy 函数,会调用 SetEvent 触发事件,然后 WaitForMultipleObjects 等待所有线程结束(注意到第三个参数为 true)。然后线程函数 ThreadFunc 探测到这个事件,会从 WaitForMultipleObjects(注意到第三个参数为 false) 返回,执行下面 ABORT_EVENT_INDEX 处理,结束线程。当所有的线程结束,接着 Destroy 的 WaitForMultipleObjects 返回,做些清理的工作即结束线程管理。
因为信号量和事件都是 HANDLE,所以该类中把这两个存在一个数组 m_phSincObjectsArray,通过枚举型变量来标识。
3、生产者 m_pWorkItemQueue
用户通过 InsertWorkItem 插入工作任务到队列。
bool CWorkQueue::InsertWorkItem(WorkItemBase* pWorkItem)
工作任务由要线程执行的函数,和 Abort 函数,Abort 函数用于最后调用 Destroy 突然终止线程时,执行剩余线程的清理工作。
class WorkItemBase
{
virtual void DoWork(void* pThreadContext) = ;
virtual void Abort () = ;
friend CWorkQueue;
};
WorkItemBase 需要用户来继承,并实现两个虚函数。
class SpecificWorkItem : public WorkItemBase
{
void DoWork(void* pThreadContext);
void Abort(); //memeber variables needed here
}; void SpecificWorkItem::DoWork(void* pThreadContext)
{
//Notify Start
//proccessing done here
//Notify Finish
//free all that was occupied
} void SpecificWorkItem::Abort()
{
//Notify aborted
//free all that was occupied
}
这样在用户执行 InsertWorkItem 插入任务到队列后,通过 ReleaseSemaphore 激发信号量 Semaphore,线程 ThreadFunc 会在 WaitForMultipleObjects 中探测到 Semaphore 的激发状态,然后获得队列中的任务,并删除队列中该任务防止其它线程重复执行,然后执行用户的任务 pWorkItem->DoWork(pThreadData),此处 pThreadData 我没用到,为 NULL 。
4、消费者 ThreadFunc
消费者线程 ThreadFunc 在 Create 中创建,返回在线程句柄存储在类成员数组 m_phThreads 中,供 Destroy 函数等待线程结束时使用。
DWORD dwThreadId;
PTHREAD_CONTEXT pThreadsContext ; //创建所有的线程
for(i = ; i < nNumberOfThreads ; i++ )
{
//初始化每个线程的上下文,用于传递给线程函数
pThreadsContext = new THREAD_CONTEXT;
pThreadsContext->pWorkQueue = this; //传递当前对象的指针,使新建线程可以访问到主线程中的生产者资源
pThreadsContext->pThreadData = ThreadData == NULL? NULL : ThreadData[i];
//创建线程
m_phThreads[i] = CreateThread(NULL,
,
CWorkQueue::ThreadFunc,
pThreadsContext,
, //为0表示线程创建之后立即就可以进行调度
&dwThreadId);
}
5、由消费者线程去访问生产者资源可以看出,同一个进程的线程可共享内存。用户可以通过 SpecificWorkItem 构造函数把数据和函数传进成员变量和成员函数,通过 InsertWorkItem 把 SpecificWorkItem 传进 CWorkQueue 的 m_pWorkItemQueue,即生产者任务队列,然后消费者线程会获得通知,并访问生产者提取任务,执行函数和数据。
参考资料:
1、http://www.codeproject.com/Articles/3607/Work-Queue
理解多线程管理类 CWorkQueue的更多相关文章
- 【Unity3D游戏开发】之全局管理类的几种方式 (十六)
如何在Unity中实现全局管理类?由于Unity脚本的运行机制和面向组件编程(COP)的思想,实现起来和普通的方式略有差别. 第一种方式是使用静态类.适合存储一些全局的变量,如游戏当前关卡.玩家得分等 ...
- Java多线程——ThreadLocal类
一.概述 ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名 ...
- 【转】【Unity】实现全局管理类的几种方式
本文原作者未知,转载自:http://blog.csdn.net/ycl295644/article/details/42458477 如何在Unity中实现全局管理类?由于Unity脚本的运行机制和 ...
- Unity中实现全局管理类的几种方式
(搬运自我在SegmentFault的博客) 如何在Unity中实现全局管理类?由于Unity脚本的运行机制和面向组件编程(COP)的思想,实现起来和普通的方式略有差别. 第一种方式是使用静态类.适合 ...
- Unity协程(Coroutine)管理类——TaskManager工具分享
博客分类: Unity3D插件学习,工具分享 源码分析 Unity协程(Coroutine)管理类——TaskManager工具分享 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处 ...
- [转]3天搞定的小型B/S内部管理类软件定制开发项目【软件开发实战10步骤详解】
本文转自:http://www.cnblogs.com/jirigala/archive/2010/10/07/1845275.html 2010-10-07 21:39 by 通用C#系统架构, 5 ...
- 深入理解多线程(五)—— Java虚拟机的锁优化技术
本文是<深入理解多线程>的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理. 前情提要 通过前面几篇文章,我们已经知道: 1.同 ...
- 深入理解多线程(四)—— Moniter的实现原理
在深入理解多线程(一)——Synchronized的实现原理中介绍过关于Synchronize的实现原理,无论是同步方法还是同步代码块,无论是ACC_SYNCHRONIZED还是monitorente ...
- C# 多线程编程第一步——理解多线程
一.进程.线程及多线程的概念 什么是多线程呢?不理解. 那什么是线程呢?说到线程就不得不说说进程.我在网上搜索也搜索了一些资料,大部分所说的进程其实是很抽象的东西.通俗的来讲,进程就是一个应用程序开始 ...
随机推荐
- CF#301 D:Bad Luck Island (概率dp)
D:Bad Luck Island 一个岛上有r个石头,s个剪子,p个布,他们之间随机挑出两个相遇,如果不是相同物种,就会有一个消失,分别求出最后这座岛上只剩下一个物种的概率. 我们用dp[i][j] ...
- python web框架 Django 登录页面
在django 项目下 创建一个templates 放模板的文件夹 html文件都放在这里 在里面写一个login.html 登录页面 urls.py 加上 login 对应关系 from djang ...
- livego
1.安装golang开发环境 https://www.cnblogs.com/eczhou/p/7929693.html 2.livego源码及说明文档 https://github.com/zhou ...
- keeplived + mysql双主复制部署 --原创
环境: master 1: 192.168.100.10 oracle linux 7.4 mysql 5.7.1 master 2: 192.168.100.11 oracle linux ...
- vue项目中多个入口的配置
出处:http://www.qingpingshan.com/jb/javascript/221105.html 基于vue2.0生成项目,一段时间都在找如何配置成多个页面的.网上有这样的例子相对也是 ...
- Linux优雅退出问题
问题:Springboot框架开发的项目中会内嵌tomcat容器,在杀死进程的时候tomcat为被正常杀死,导致端口未被释放,第二次启动的时候报端口冲突. 先讲一个基本概念:如何在shell中终止一个 ...
- 安装es6转码工具babel,具体步骤
新建文件夹:testBabel 全局安装Babel:npm install babel-cli -g //babel的命令会成为全局变量,可以直接使用,一般全局后还要装本地(本工程目录) 初始化:np ...
- java synchronized关键字的底层实现
每个对象都有一个锁(Monitor,监视器锁),class对象也有锁,如果synchronized关键字修饰同步代码块,通过反编译可以看到,其实是有个monitorenter和monitorexit指 ...
- windows上使用clang编译程序
环境:windows7,64位 1.下载并安装llvm,安装包里除了llvm,也有clang: http://releases.llvm.org/5.0.0/LLVM-5.0.0-win64.exe ...
- Python3.x:chrome运行webdriver脚本提示--ignore-certificate-errors
Python3.x:chrome运行webdriver脚本提示--ignore-certificate-errors 1,分析原因: 根本原因是Chromedriver和Chrome的版本不兼容: 网 ...