源码见:http://www.codeproject.com/threads/multi_threaded_job_queue.asp
InterlockedIncrement、InterlockedDecrement用于对多个线程竞争的公共资源进行排他式的增减1。此例中的lastUserID是父类CJob中的static变量,对于CJob的所有对象有且只有一份,其用于对已分配的编号进行计数,故应用InterlockedIncrement、InterlockedDecrement。
ON_REGISTERED_MESSAGE(REFRESH_LIST,OnRefreshList)用于向系统声明代号为REFRESH_LIST的消息,并指出对该消息的处理函数为OnRefreshList。
AfxGetApp()用于得到指向应用程序中实际的CWinApp指针。
JobQDlg中的Refresh()用于刷新记录等待作业的列表框的显示,由于Refresh()会在线程从等待作业链表中取走作业时被调用,故为了防止线程对列表框这一公共资源的访问,应设一个CCriticalSection,用于互斥访问。
BOOL UpdateData(BOOL bSaveAndValidate = TRUE)的用法:若bSaveAndValidate为TRUE,则表示即时更新相关控件对应的变量值;若bSaveAndValidate为FALSE,则表示根据当前的变量值去设置控件。
typedef CTypedPtrList<CPtrList,CJob*> CJobQList的用法:CTypedPtrList是一种类型安全的链表,其中第一个模板型参数可为CObList或CPtrList,第二个模板型参数相应地为实际想要存储的对象或对象指针。
CMThreadJobQ是整个工程中最为重要的类,其中包含一个m_pObserverThread(观察者线程指针,用于观察并及时将工作分配给空闲的工作线程),一个m_jobQList(用于存储等待作业的链表),一个m_pFreeEList(用于存储空闲工作线程的链表),一个m_pAllEList(用于存储全部工作线程的链表)。
static UINT REFRESH_LIST = ::RegisterWindowMessage("REFRESH LIST")的用法:向系统注册消息,得到该字符串所代表的消息的代码,一般会与ON_REGISTERED_MESSAGE(REFRESH_LIST,OnRefreshList)配合使用。
m_pObserverThread = AfxBeginThread(JobObserverThreadFunction,this)的用法:AfxBeginThread的第一个参数为线程函数,第二个参数为传给该线程函数的变量。
观察者线程函数的处理流程大致为:(1)判断有没有禁止处理新作业,若禁止,则挂起观察线程;否则进入第二步;(2)只要等待作业队列不为空且还有空闲工作线程,则循环处理之,否则跳出该层循环(为了及时相应禁止选项设置带来的影响,该层循环中也要判断是否禁止处理新作业)。
工作线程函数的处理流程中的注意要点:首先执行工作线程所指定的工作,随后判断当前工作线程总数(包括空闲的工作线程)和最大允许的工作线程总数,若前者大于后者,则从工作线程链表中将其删除,并重置当前工作线程总数;否则将该工作线程(目前已完成工作)加入空闲工作线程链表并挂起之,同时需要重启观察者线程以告之已有空闲工作线程。
CPrgUIThread是UI线程,其继承于CWinThread,并重写了InitInstance(),其中生成了包含进度条的对话框。而启动UI线程的方式也很简单,即CPrgUIThread* pTh = (CPrgUIThread*)AfxBeginThread(RUNTIME_CLASS(CPrgUIThread))。
发现其中一个bug:在工作线程处理函数中,若当前工作线程总数(包括空闲的工作线程)大于最大允许的工作线程总数,只需从工作线程链表中将其删除并重置当前工作线程总数,而作者却重启该工作线程,而正确的做法是挂起该进程。(详见JobExecuter中的stop())