WINDOWS核心编程——线程池和纤程

微软这是对开发者友好的的软件厂商,在直接提供线程池使得开发者在需要使用线程池时无需自己编写(特别是调度的部分),用户无需自己创建和管理线程。微软默认用户使用线程池与不同的对象关联来满足四种需求:

1.以异步的方式调用一个函数。用户需要异步调用一个函数,主要将这个函数地址传给线程池,系统就会在合适的时间调用它。函数地址当然必须是接口定义好的函数指针。

//回调函数指针类型
VOID NTAPI ThreadFunc(
    PTP_CALLBACK_INSTANCE pInstance,
    PVOID pvContext
);  
//将回调函数传递到线程池中
BOOL TrySubmitThreadpoolCallback(
    PTP_SIMPLE_CALLBACK pfnCallback, //上边指定的回调函数
    PVOID pvContext,  //传递给回调函数的参数
    PTP_CALLBACK_ENVIRON pche
);  
TrySubmitThreadpoolCallback的主要工作就是创建一个(包含回调函数地址和参数的)线程池工作对象,并将工作对象加入到线程池中,由于用户环境的多样性函数TrySubmitThreadpoolCallback可能在创建ThreadpoolWork(TP_Work)时失败,故而导致整个函数不能完成工作而返回false。所以我们可以自己创建一个工作对象并自己添加到线程池中来完成这个过程,并处理失败的情况。使用如下函数:

//回调函数指针类型
VOID CALLBACK WorkCallback(  
    PTP_CALLBACK_INSTANCE Instance,  
    PVOID Context,  
    PTP_WORK Work //调用这个回调的工作对象
);
//创建工作对象
PTP_WORK CreateThreadpoolWork(
    PTP_WORK_CALLBACK pfnWorkHandler,  //上边这个类型的函数指针
    PVOID pvContext,  // 回调的参数
    PTP_CALLBACK_ENVIRON pche
);
//把工作对象提交给线程池(这个函数不会失败)
VOID SubmitThreadpoolWork(PTP_WORK pWork);
对于同步问题我们可以调用函数等待或取消工作对象的调用:

VOID WaitForThreadpoolWorkCallbacks(  
    PTP_WORK pWork, //目标工作对象 
    BOOL bCancelPendingCallbacks //false,等到回调结束,true若回调在执行过程中等到它执行结束,否则标记为取消不会再被执行
);
//使用完工作对象之后要删除它
VOID CloseThreadpoolWork(PTP_WORK pwk);  
和所有的wait系列函数一样,除非是在回调未执行的时候取消掉他,不然wait函数总是会阻塞当前线程直到对象被触发。

2.每隔一段时间调用一个函数。与异步调用一样用户通过创建一个定时器对象并传递给线程池,线程池根据定时器是否到达来做出是否调用回调函数的决定(而不是是否有空闲的线程)使用的接口也与异步调用类似:

//回调函数指针类型
VOID CALLBACK TimeoutCallback(  
    PTP_CALLBACK_INSTANCE pInstance,  
    PVOID pvContext,  
    PTP_TIMER pTimer //导致回调的定时器
);
//创建(线程池能够识别的)定时器
PTP_TIMER CreateThreadpoolTimer(  
    PTP_TIMER_CALLBACK pfnTimerCallback,  //上边这个类型的函数指针
    PVOID pvContext,    // 回调的参数
    PTP_CALLBACK_ENVIRON pcbe
);
//把定时器设置到线程池中
VOID SetThreadpoolTimer(  
    PTP_TIMER pTimer,  //定时器对象
    PFILETIME pftDueTime,  //第一次执行回调的时间(基准),-1标识理解开始执行
    DWORD msPeriod,  //时间周期单位为微秒,0表示不再执行
    DWORD msWindowLength //时间窗口,既执行完成之后不立即休眠等待一段时间(以避免线程sleep后在唤醒的代价)
);
//检查定时器对象是否被设置了(pftDueTime不为NULL)
BOOL IsThreadpoolTimerSet(PTP_TIMER pti);  
//等待线程执行结果与WaitForThreadpoolWorkCallbacks一样
VOID WINAPI WaitForThreadpoolTimerCallbacks(  
    PTP_TIMER pti,  
    BOOL fCancelPendingCallbacks
);
//使用完之后删除
VOID WINAPI CloseThreadpoolTimer(  
    PTP_TIMER pti
);

3.当内核对象触发时调用一个函数。在线程池的线程中调用WaitForMultipleOBjecs(故不允许同一个内核对象多次被加入到list中)等待内核对象的触发,触发后调用回调函数,并解除等待对象与内核对象的关联(不再监视内核对象,若需要再次监视就需要再次调用SetThreadpoolWait)。

//回调函数指针类型
VOID CALLBACK WaitCallback(
    PTP_CALLBACK_INSTANCE Instance,
    PVOID Context,
    PTP_WAIT Wait, //导致回调的等待对象
    TP_WAIT_RESULT WaitResult //等待对象的等待结果
);
//创建线程池等待对象
PTP_WAIT WINAPI CreateThreadpoolWait(
    PTP_WAIT_CALLBACK pfnwa, //上边这个类型的函数指针
    PVOID pv, // 回调的参数
    PTP_CALLBACK_ENVIRON pcbe
);
//把等待对象和内核对象句柄关联起来并设置到线程池中
VOID WINAPI SetThreadpoolWait(
    PTP_WAIT pwa, //等待对象
    HANDLE h, //要监视的内核句柄, 传入NULL时从线程池中删除等待对象
    PFILETIME pftTimeout //等待时间
);
//等待线程执行结果与WaitForThreadpoolWorkCallbacks一样
VOID WINAPI WaitForThreadpoolWaitCallbacks(
    PTP_WAIT pwa,
    BOOL fCancelPendingCallbacks
);
//使用完之后删除
VOID WINAPI CloseThreadpoolWait(
    PTP_WAIT pwa
);
回调函数的waitresult参数表示等待的结果:



4.当异步IO请求完成时调用一个函数。在异步IO中详细了解过,同样的使用方式与前集中差别不大:

//回调函数指针类型
VOID CALLBACK OverlappedCompletionRoutine(
    PTP_CALLBACK_INSTANCE pInstance,
    PVOID pvContext,
    PVOID pOverlapped,
    ULONG IoResult,
    ULONG_PTR NumberOfBytesTransferred,
    PTP_IO  pIo
);  
//创建完成端口
PTP_IO CreateThreadpoolIo(
    HANDLE hDevice,
    PTP_WIN32_IO_CALLBACK pfnIoCallback,
    PVOID pvContext,
    PTP_CALLBACK_ENVIRON pcbe
);  
//把完成端口设置到线程池中
VOID StartThreadpoolIo(PTP_IO pio);  
//等待完成端口结果与WaitForThreadpoolWorkCallbacks一样
VOID WaitForThreadpoolIoCallback(
    PTP_IO pio,
    BOOL bCancelPendingCallbacks
);
//取消完成端口
VOID CancelThreadpoolIo(PTP_IO pio);  
//使用完之后删除
VOID CloseHandlepoolIo(PTP_IO pio);  
对于各个回调接口中的pInstance在指定回调返回后的指定动作时使用,回调返回后可以释放一些锁:

虽然用到的不多,windows还是提供了自己定制线程池的方法,首先要创建线程池:

//创建新线程池
PTP_POOL CreateThreadpool(PVOID reserved);  
//设置线程池中的最大线程和最小线程
void SetThreadpoolThreadMinimum(PTP_POOL pThreadpool,DWORD cthrdMin);
void SetThreadpoolThreadMaximum(PTP_POOL pThreadpool,DWORD cthrdMin);  
为了让四种对象加入到我们自己所创建的线程池中还需要自定义的回调环境,作为Create这种变量时的参数PTP_CALLBACK_ENVIRON pcbe,此参数若为NULL则与默认线程池相关联,回调环境接口如下:

//初始化回调环境
VOID InitializeThreadpoolEnvironment(PTP_CALLBACK_ENVIRON pcbe); 
//将回调环境与线程池进行关联
VOID SetThreadpoolCallbackpoo(PTP_CALLBACK_ENVIRON pche, PTP_POOL pThreadPool);
接下来就能想使用默认线程池一样的使用自定义的线程池,当时在线程池使用完成后需要自己清理(默认的生命周期与应用程序一样长,系统会清理),要创建一个清理组:

//创建一个清理组
PTP_CLEANUP_GROUP CreateThreadpoolCleanuGroup();  
//将清理组和线程池相关联
VOID SetThreadpoolCallbackCleanupGroup(
    PTP_CALLBACK_ENVIRON pcbe,
    PTP_CLEANUP_GROUP ptpcg,
    PTP_CLEANUP_GROUP_CANCEL_CALLBACK pfng);
//SetThreadpoolCallbackCleanupGroup的回调
VOID CALLBACK CleanupGroupCancelback(
    PVOID pvObjectContext,
    PVOID pvCleanupContext);
当我们调用CloseThreadpoolWork,CloseThreadpoolTimer,CloseThreadpoolWait或是CloseThreadIo时,等于是隐式的将对应项从清理组中清理。但是结束线程池之前还是需要做一次清理才结束线程以防资源的泄漏:

//清理线程池
VOID CloseThreadpoolCleanupGroupMemebers(
    PTP_CLEANUPGROUP ptpcg,
    BOOL bCancelPendingCallbacks,
    PVOID pvCleanupContext); 
    
//释放清理组所占的资源
VOID WINAPI CloseThreadpoolCleanupGroup(PTP_CLEANUP_GROUP ptpcg);  

//销毁回调环境
VOID DestroyThreadEnvironment(PTP_CALLBACK_ENVRION pcbe);  
//销毁 所有线程都被终止所有项将被取消
VOID CloseThreadpool(PTP_POOL pThreadpool); 

纤程我们只需要概念上理解就好了,一般也用不到。就是一个可执行的对象(函数对象?)自己包含了执行的状态(栈和寄存器)。被我们自己设计的逻辑所调度(switchto函数)。

好的,关于CPU多核编程——线程池开发方面的问题,我可以向您提供一些建议和思路。 线程池是一种常用的并发编程技术,其主要作用是在多核CPU的情况下,实现任务的并行执行,提高程序的性能。在实际开发中,线程池通常都采用生产者-消费者模式进行开发。 具体来说,线程池中有一个任务队列,负责存放待执行的任务。线程池内部有一些工作线程,它们会不断地从任务队列中取出任务进行执行。如果任务队列为空,那么工作线程会被挂起,等待新的任务到来。 在开发线程池时,关键是要实现线程的管理和同步。具体工作包括: 1. 配置线程池中工作线程的数量。这个数量通常要根据CPU的核心数和任务的类型来决定。如果任务是I/O密集型,那么工作线程数量应该设置得比较大;如果任务是计算密集型,那么工作线程数量可以适当减少。 2. 实现任务队列。这个队列可以采用多种数据结构来实现,比如数组、链表、堆等。对于多线程的情况,需要考虑线程之间的同步问题,防止数据竞争。 3. 实现工作线程。这些线程需要不断地从任务队列中取出任务进行执行,并且在没有任务时进入等待状态。同时,为了保证线程之间的公平性,需要采用一些调度算法,比如轮询、优先级队列等。 4. 实现任务接口。这些接口需要包括任务的执行函数以及相关的参数,以支持不同类型的任务。 总体来说,线程池的开发比较复杂,需要在多线程的情况下考虑到各种同步和调度问题。但是一旦实现成功,线程池可以大大提高程序的运行效率和响应速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值