1、I/O完成端口可以非常智能的分派线程,但是IO完成端口仅对等待它的线程进行分派,创建和销毁线程的工作仍然需要我们自己来做。
我们自己也可以创建线程,但是涉及到线程的编码操作比较复杂,容易出现差错。为了简化程序员的工作,Windows提供了一个线程池机制来简化线程的创建、销毁以及日常管理。这个新线程池可能不适用于所有的情况,但大多数情况下它都能够满足我们的需要。
2、这个线程池能够帮助我们做一下事情:
1)以异步的方式调用一个函数。
2)每隔一段时间调用一个函数。
3)当内核对象触发时调用一个函数。
4)当异步IO请求完成时调用一个函数。
情形一:以异步方式调用函数。
1、让线程池执行的函数需要遵循一下原型:
VOID NTAPI ThreadFunc(
PTP_CALLBACK_INSTANCE pInstance,PVOID pvContext);
2、定义了线程池线程入口函数,就需要提交请求让线程池执行该函数:
BOOL TrySubmitThreadpoolCallback(
PTP_SIMPLE_CALLBACK pfnCallback,PVOID pvContext,
PTP_CALLBACK_ENVIRON pche);
该函数(通过调用PostQueuedCompletionStatus来)将一个工作项添加到线程池队列中。若调用成功,则返回true。否则返回false。
---pfnCallback表示线程池线程入口函数。即我们上面定义的函数。
---pvContext是传给线程入口函数的参数。
---pche可以先传给它NULL。在后面我们还会有详细的介绍。
3、当我们提交一个请求后,系统就会创建一个默认的线程池并让线程池的一个线程来调用回调函数。并不需要我们手动调用CreateThread。当线程从入口函数返回时,并不会销毁而是返回到线程池。线程池会不断重复使用各个线程,而不会频繁销毁和新建线程。这显著的提高了性能。
4、在某些情况下,如内存不足时TrySubmitThreadpoolCallback可能会失败。每一次调用TrySubmitThreadpoolCallback时,系统会在内部分配一个工作项。如果打算提交大量的工作项,出于性能和内存使用方面的考虑,应该手动创建工作项然后多次提交它。
5、下面的函数创建一个工作项:
PTP_WORK CreateThreadpoolWork(
PTP_WORK_CALLBACK pfnWorkHandler,PVOID pvContext,
PTP_CALLBACK_ENVIRON pche);
此函数会在用户模式内存中创建一个结构来保存它的三个参数,并返回指向该结构的指针。
---pfnWorkHandler是一个函数指针,当线程池中的线程最终对工作项进行处理时会调用该函数。该函数必须遵循一下函数原型:
VOID CALLBACK WorkCallback(
PTP_CALLBACK_INSTANCE Instance,PVOID Context,
PTP_WORK Work);
---pvContext是传给回调函数(pfnWorkHandler)的参数(任意值)。
6、我们可以调用SubmitThreadpoolWork向线程池提交一个请求:
VOID SubmitThreadpoolWork(PTP_WORK pWork);
7、如果我们想取消已经提交的工作项或是等待工作项处理完毕。可以调用以下函数:
VOID WaitForThreadpoolWorkCallbacks(
PTP_WORK pWork,
BOOL bCancelPendingCallbacks);
此函数将线程挂起,直到工作项处理完毕。
---pWork指向一个工作项。此工作项可以是CreateThreadpoolWork和SubmitThreadpoolWork来创建和提交的。如果工作项尚未被提交,那么等待函数立即返回。
如果传入true给bCancelPendingCallbacks,那么WaitForThreadpoolWorkCallbacks会试图取消pWork标识的工作项。如果线程正在处理此工作项,则不会取消,而等待工作项处理完毕后返回。如果工作项还未被处理,函数会将此工作项标记为已取消,然后立即返回。
如果传入false给bCancelPendingCallbacks那么WaiForThreadpoolWorkCallbacks会将线程挂起,直到工作项处理完毕。
如果用一个PTP_WORK提交了多个工作项,传给bCancelPendingCallbacks为false,那么等待函数会等待所有的工作项都被处理完毕。
8、当不需要一个工作项时,可以调用CloseThreadpoolWork,并传入指向该工作项的指针。
VOID CloseThreadpoolWork(PTP_WORK pwk);
例子:该例展示了如何使用线程池的工作项。
点击开始提交四次同一个工作项。
当回调函数返回时,程序将向程序发送自定义的TASK_COMPELETED消息。然后该消息处理函数将项下拉列表添加一项。
在MFC中手动添加消息需要一下步骤:
1)定义消息:如#defined TASK_COMPELETED WM_USER+1
2)定义消息处理函数原型:
afx_msg LRESULT OnTaskCompeleted(WPARAM wparam,LPARAM lparam);
3)调用PostMessage发送消息。
PostMessage(this,TASK_COMPELETED,0,(LPARAMm_CurrentTask);
4)在消息映射表中添加消息和消息处理函数的映射:
开始按钮消息处理函数:
- void CThreadpoolexap1Dlg::OnBnClickedBtnStart()
- {
- m_WorkItem=CreateThreadpoolWork(ThreadFunc,this,NULL);
- if(m_WorkItem==NULL)
- {
- MessageBox(TEXT("工作项创建失败"));
- return ;
- }
- // TODO: 在此添加控件通知处理程序代码
- SubmitThreadpoolWork(m_WorkItem);
- SubmitThreadpoolWork(m_WorkItem);
- SubmitThreadpoolWork(m_WorkItem);
- SubmitThreadpoolWork(m_WorkItem);
- }
线程入口函数:
- VOID NTAPI CThreadpoolexap1Dlg::ThreadFunc( PTP_CALLBACK_INSTANCE Instance,PVOID Context,PTP_WORK Work )
- {