Windows核心编程学习笔记(22)--Windows线程池

Drecik学习经验分享

转载请注明出处:http://blog.csdn.net/drecik__/article/details/8194020


Windows线程提供了一个线程池机制来简化线程的创建,销毁以及日常管理,避免了线程频繁创建和销毁的开销。

一下介绍的与现场池有关的函数是新的线程池API,只能运行在Windwos Vista及以后版本。

这些线程池函数可以帮助我们做以下事情:

  • 以异步的方式来调用一个函数
  • 每隔一段时间调用一个函数
  • 当内核对象出发的时候调用一个函数
  • 当异步I/O请求完成的时候调用一个函数
我们只讨论使用默认线程池。

1. 以异步的方式来调用一个函数

有两种方式实现该过程,下面分别讲述:

  • 第一种方法,首先定义一个具有以下原型的函数:
    typedef VOID (NTAPI *PTP_SIMPLE_CALLBACK)(
    	PTP_CALLBACK_INSTANCE Instance,	// 用户回调函数终止操作,我也没看懂,传NULL吧,一般操作都够用了;
    	PVOID                 Context	// 在加入线程池的时候由用户传入的参数;
    	);
    为了让自己定义好的函数加入到线程池中,让线程执行我们的函数,我们需要向线程池提交请求:
    BOOL
    TrySubmitThreadpoolCallback(
    	PTP_SIMPLE_CALLBACK  pfns,	// 自己定义的函数;
    	PVOID                pv,	// 传给自己定义函数的Context参数;
    	PTP_CALLBACK_ENVIRON pcbe	// 用来自己创建线程池,使用默认线程池传入NULL;
    	);
    加入成功后,线程池就会有线程来执行我们的函数。
  • 第二种方法:由于每一次调用TrySubmitThreadpoolCallback系统内部的都会以我们的名义分配一个工作项,如果打算提交大量的工作项,那么可以处于性能和内存使用的考虑,创建工作性一次,然后分多次提交它会更好。
我们使用CreateThreadpoolWork创建一个工作项:
PTP_WORK
CreateThreadpoolWork(
	PTP_WORK_CALLBACK    pfnwk,	// 工作项调用的函数;
	PVOID                pv,	// 传给调用函数的参数;
	PTP_CALLBACK_ENVIRON pcbe
	);

// PTP_WORK_CALLBACK函数原型;
typedef VOID (NTAPI *PTP_WORK_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,	// 上面函数传进来的pv参数;
	PTP_WORK              Work		// 关联的工作项;
	);

// 向线程池提交一个请求的时候调用下面函数;
// 提交成功后,线程池会有线程来执行我们创建的函数;
VOID
SubmitThreadpoolWork(
	PTP_WORK pwk		// 工作项;
	);

多次提交唯一的缺点就是每一次传给自定义函数的Context值都相同,如果需要不同的值只能使用TrySubmitThreadpoolCallback函数。

如果我们有另外一个线程,该线程负责取消已经提交的工作项,或者等待工作项处理完毕而需要将自己挂起,则需要调用下面函数:

// 如果工作项未提交,函数就立即返回不执行任何操作;
// 第二个参数如果为TRUE,那么该函数会试图取消先前提交的那个工作项;
// 如果线程池中的线程正在处理那个工作项,该函数会一直等待到工作项完成后才返回;
// 如果已经提交的工作项尚未被任何线程处理,那么会将它标记会取消,并立即返回,这样就不会被执行;
// 如果为FALSE,那么将会挂起,知道指定工作项的处理已经完成,而且线程池处理该工作项的线程都已经被收回位置;
// 如果用一个PTP_WORK对象提交了多个工作项,传给第二个参数为FALSE,就会等待所有工作项完成;
// 如果为TRUE,只会等待当前正在运行的工作项;
VOID
WaitForThreadpoolWorkCallbacks(
	PTP_WORK pwk,	// 待取消或等待的工作项;
	BOOL     fCancelPendingCallbacks
	);

// 最后一个函数关闭创建的工作项;
VOID
CloseThreadpoolWork(
	PTP_WORK pwk
	);

2. 每个一段时间调用一个函数

① 首先我们必须定义一个如下原型的回调函数:
typedef VOID (NTAPI *PTP_TIMER_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,	// 传入的参数;
	PTP_TIMER             Timer		// 关联的TIME指针;
	);
② 创建一个PTP_TIMER对象:
PTP_TIMER
CreateThreadpoolTimer(
	PTP_TIMER_CALLBACK   pfnti,	// 自己定义的函数指针;
	PVOID                pv,	// 传入自己定义的函数的参数;
	PTP_CALLBACK_ENVIRON pcbe
	);
③ 注册计时器:
// 其中开始时间传入负值表示从现在开始多长时间第一次触发;
// 如果为正值,表示一个绝对时间,从1600年1月1日开始计算;
// 单位都为100纳秒;
VOID
SetThreadpoolTimer(
	PTP_TIMER pti,				// 创建的计时器对象;
	PFILETIME pftDueTime,		// 开始时间;
	DWORD     msPeriod,			// 每隔多少时间触发一次,只触发一次传入0;
	DWORD     msWindowLength	// 该参数可以指定在msPeriod+msWindowLength这一时间段内触发,防止冲突;
	);
设置了计时器之后,还可以调用SetThreadpoolTimer来对计时器进行修改,也可以在pftDueTimer传入NULL表示停止调用回调函数。

通过IsThreadpoolTimerSet来判断某个计时器是否被设置。

最后两个函数WaitForThreadpoolTimerCallbacks和CloseThradpoolTimer与之前讨论过的类似,不在讨论。


3. 在内核对象出发时调用一个函数

这些线程池操作基本上都大同小异,所以有些参数不在解释:

// 回调函数;
// 第四个参数表示事件触发的类型,WAIT_OBJECT_0:关联的事件触发;
// WAIT_TIMEOUT:等待超时,WAIT_ABANDONED_0:互斥量遗弃(参考以前的博文);
VOID (NTAPI *PTP_WAIT_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,
	PTP_WAIT              Wait,
	TP_WAIT_RESULT        WaitResult
	);

PTP_WAIT
CreateThreadpoolWait(
	PTP_WAIT_CALLBACK    pfnwa,
	PVOID                pv,
	PTP_CALLBACK_ENVIRON pcbe
	);

// 关联内核对象;
// 可多次调用关联多个事件,但其中一个触发就会调用回调函数;
// 第三个参数表示等待时间,0表示不等待,NULL表示无限等待,正数表示绝对时间,负数相对时间;
VOID
SetThreadpoolWait(
	PTP_WAIT  pwa,
	HANDLE    h,			// 内核对象;
	PFILETIME pftTimeout
	);

一旦调用回调函数,对应的等待项将不活跃,必须重新关联该内核对象。

可以在SetThreadpoolWait事件传入NULL表示将该等待项从内存池中移除。

最后WaitForThreadpoolWaitCallbacks等待等待项完成,CloseThreadpoolWait释放等待项内存。

最好不要使用PulseEvent触发事件,因为无法保证线程池正好在等待该事件。

4. 在异步I/O请求完成时调用一个函数

typedef VOID (WINAPI *PTP_WIN32_IO_CALLBACK)(
	PTP_CALLBACK_INSTANCE Instance,
	PVOID                 Context,
	PVOID                 Overlapped,		// 异步IO传入的OVERLAPPED结构指针;
	ULONG                 IoResult,			// 是否有错误,NO_ERROR表示没有错误;
	ULONG_PTR             NumberOfBytesTransferred,	// 传输的字节;
	PTP_IO                Io
	);

PTP_IO
CreateThreadpoolIo(
	HANDLE                fl,		// 设备句柄;
	PTP_WIN32_IO_CALLBACK pfnio,	// 回调函数;
	PVOID                 pv,
	PTP_CALLBACK_ENVIRON  pcbe
	);

// 每次发送异步I/O请求必须与线程池关联;
VOID
StartThreadpoolIo(
	PTP_IO pio
	);

// 在发出请求之后让线程停止调用回调函数;
// 如果发送请求的时候调用失败,则仍然必须调用下面函数;
VOID
	CancelThreadpoolIo(
	PTP_IO pio
	);

// 在关闭设备对象的时候,解除与内存池的关联;
VOID
	CloseThreadpoolIo(
	PTP_IO pio
	);
同样还有WaitForThreadpoolIoCallbacks来等待请求的完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值