线程受限资源的关闭
全库目录221
本文目录6
解码器A运行于插件M,借用缓存B,并在界面线程T持有窗口与COM接口I。A通过I建立订阅,窗口销毁通知还要经I归还通知令牌。因此收尾顺序是撤销订阅并排空业务回调、销毁窗口并等待同步回调退出、释放I,最后才析构A并归还B的缓存。本文沿这条资源依赖解释线程派发和异步结果对账。模块从Closing进入Draining完成准备与排空,再进入Destroying执行析构,最后到Unloaded;状态推进见 插件关闭与卸载。关闭步骤使用Ready、Queued、Invoking、Waiting、Completed,分别表示可执行、已排队、正在调用、等待结果或排空、已完成。步骤记录与所有权交接见 插件接口与对象借用。
清理线程与关闭派发
宿主协调者H从单对象协调者S接管关闭记录C_A后,以所有权代次4推进步骤;原异步请求仍以原接纳凭据对账。
解码器A持有的窗口和COM接口在界面线程T清理。线程约束与寿命是两件事:保留模块使代码可调用,不能让任意线程取得操作窗口或接口的权限。对象登记时同时保存RequestStop、显式收尾和Destroy各自的线程或Apartment要求。协调者据此选择执行位置,调用步骤仍由唯一关闭记录保护。
T先执行订阅撤销,再在Draining中按资源依赖关闭窗口并释放COM引用。收尾步骤及其同步清理回调另持步骤保护,它们不属于已经关闭的业务访问集合,否则会出现收尾等待自己退出的循环。宿主只允许绑定当前步骤、目标资源及对象代次的清理重入;回调返回后才能保存收尾完成。
A发布时为订阅、窗口和每份COM引用准备稳定资源步骤ID及结果空间,将本文开头的资源依赖登记到关闭记录。前驱步骤完成并退出后才可授权后继;资源依赖与对象A→B的寿命关系分别保存。
停止准备中,T先通过I撤销订阅,并使对应宿主回调来源停止接纳。撤销结果单独登记;已经排队的该来源回调由宿主跳板拒绝普通执行并归还保护,已进入的回调仍持使用令牌。取消来源属于停止与排空准备,窗口和I此时都保留。若撤销异步完成,订阅步骤保持Waiting,由T继续泵消息,完成回执按下一小节的对账入口进入宿主收件表;确定失败时保留订阅身份与I,按尝试契约决定重试。只有撤销确认且该来源回调全部退出,才把订阅步骤记Completed并授权窗口清理。
异步撤销的结果对账
撤销订阅可能在T调用返回后才完成。每个撤销步骤保存C_A、A对象代次、订阅ID与代次、稳定步骤ID、尝试ID、参数哈希和提供方任务ID。提交调用前登记Prepared,并把尝试ID作为提供方支持的幂等键或可查询关联键;取得任务ID后登记Accepted。响应丢失时先用关联键找回任务ID,无法关联的接口进入后文Unknown处理。
同步调用返回只结束Invoking,未取得终态的步骤转Waiting,并单独保留异步活动保护、I引用和结果接收记录。这些保护在调用前预留并登记,在途调用保护与异步等待保护的交接共同记录,避免调用返回后出现无人保留资源的空档。协调者S交出Waiting时,原尝试与保护一起交给H,不能当作任务已经退出。
发起权限与结果入口分别核验。S只有持当前所有权代次才可派发新调用;结果经宿主常驻接收跳板进入收件表,按原尝试的接纳凭据、提供方任务ID、对象及订阅代次、载荷哈希核对。原请求在代次3被合法接纳,H接管为4后,其结果仍可入表;结果携带3不授予S继续调用的权限。跳板只保存证据和唤醒当前协调者,不访问A的普通字段,也不运行窗口清理。
结果先于接纳响应登记时,收件表暂存候选结果并按尝试关联键查询提供方,补齐接纳凭据后再消费;缺少凭据保持待对账。结果已经到达只说明有一份待核实消息,不能直接把关闭步骤记为完成。
例如撤销步骤R8的尝试U1已被提供方接纳,S在Waiting交给H。U1成功消息到达,宿主先把终态写入U1的结果槽。H在同一宿主门内比较当前所有权代次4、R8等待的尝试U1与结果消费标记,共同登记提供方撤销成功及消费完成,再等待该订阅的排队回调退出。两项都完成才将R8记Completed并允许窗口步骤。协调者再次变更时,只有取得新代次的一方能消费,结果本身仍归U1。
S随后收到旧唤醒时只能查询同一结果;U1重复成功返回原终态,不再次撤销或释放I。不同参数、错误订阅代次及并非该提供方任务的消息进入诊断,不改变当前步骤。提供方契约要求同一尝试只有一个确定终态;观察到矛盾终态时记录协议故障并暂停尚未授权的后继清理,向提供方核对。已完成的窗口或引用释放保持原回执,不能通过覆盖结果复活已释放资源。
接收过程中保护也分开归还。提供方的终态说明该尝试执行结束,结果证据入表并获宿主确认后归还该尝试的异步活动保护;当前接收跳板及仍在队列中的通知各持自己的保护,退出或取消确认后才归还。订阅业务回调的排空完成以前保留I,模块卸载还须等待全部相关跳板退出。
业务订阅的撤销完成与结果入口的关闭分别登记。本例的尝试结果路由由宿主常驻接收器管理,登记与注销均不调用I。H消费确定终态后,注销U1的结果路由并确认其通知队列排空,再结束对应接收保护;如果已排队通知仍在执行,就继续等待。终态去重记录保留到这项排空完成,晚到重复消息由宿主按原身份结清,不再回调插件。
结果入口只持身份及证据,不访问已经完成收尾的A资源,因此独立排空不会要求窗口或I重新存活。其他提供方的结果注销若仍需I,就把该注销及排空登记为I释放的前驱步骤,按同一资源依赖规则执行。
调用响应丢失时保持Unknown,H按提供方任务ID或原关联键查询。已接纳就等待或消费原终态,确认失败后可用新尝试ID重试。确定未接纳还须关闭旧派发授权,确认旧队列项退出、原调用已返回且提供方已拒绝或关闭该旧尝试,才允许新尝试;这样旧调用不会在新尝试启动后又被接纳。提供方支持幂等补发时,保持原尝试ID与参数。
无法查询且没有幂等契约时保留等待与I,报告无法确认。宿主进程内续行先消费收件表,再决定派发;对象与函数表不跨进程恢复,进程边界见 插件关闭与卸载。
窗口与接口收尾
T随后执行DestroyWindow。同步销毁回调通过仍有效的I归还预留通知令牌;本项目接口将这份归还约定为幂等、同步、不抛异常的结算,它不创建任务,也不等待订阅退出。回调读取已完成的订阅步骤,沿原回执跳过撤销。DestroyWindow成功返回且窗口销毁回调退出后,宿主共同清空窗口字段并保存窗口步骤Completed,T才领取I引用释放步骤。Release返回后清空I字段并记录Completed;A析构看到两项均空,只继续归还B的缓存页。窗口回调所需的接口因此保留到它退出。
若窗口销毁失败且窗口仍有效,保存失败回执,保留窗口及I,仅重试该窗口步骤。订阅已撤销与通知令牌已归还的结果分别沿原回执消费,不能重复结算。Release尚未Invoking时仍可交接执行权;已经Invoking就等待原调用。I释放完成后的整体收尾通知即使重复,也只返回原回执。多个资源的收尾先构成依赖图,选择前驱全部Completed且回调已退出的步骤;资源图有环时在Draining拆出显式结算步骤或使用组合资源所有者,解除循环后才授权释放。
字段更新与完成登记之间禁止异步取消执行线程。线程异常终止而无法判断释放结果时隔离该记录,保留模块,不以重试猜测。全部资源步骤完成后才保存整体收尾回执,后续Destroy只消费这个已收尾状态。本例A的析构也要求T,但要等模块进入Destroying后另行授权。
宿主在发布对象前预留停止、收尾与销毁的派发槽及结果空间,进入Destroying前再核验参数和关闭令牌;容量不足在发布前失败。发起调用的清理跳板只接受绑定C_A、具体步骤、A的对象代次和当前所有权代次4的授权;已接纳异步步骤的结果使用独立的证据入口。插件不能借清理消息创建普通业务任务;普通事件入口保持关闭。
协调者投递后暂停推进,步骤仍明确保持Queued或Invoking,T的常驻事件循环继续服务关闭消息、COM派发和必要的窗口销毁消息。T收到消息后核验步骤,再在该线程同步调用插件,返回时保存回执。协调者和T都不能阻塞等待一项必须由自身事件循环完成的工作;界面触发关闭时只登记请求并返回,宿主后续轮次继续推进。消息处理引发重入时,普通业务仍被入口拒绝,清理重入则比较步骤状态,Invoking项不能再次进入。
窗口过程也通过宿主跳板保留模块。调用DestroyWindow时,宿主在T的调用作用域保存当前步骤和目标窗口的清理权限;同步触发的WM_DESTROY和WM_NCDESTROY由跳板核对这份上下文后进入资源清理分支,完成注销和释放。普通输入与重新加载资产仍被拒绝,消息类型本身不授予清理权限。该同步窗口回调属于当前窗口清理步骤,不能再次请求同一步骤或在回调内等待整个模块卸载。
Destroying阶段由协调者一次授权一个对象的销毁步骤,必要时借上述预留通道派到T;插件函数本身同步返回。协调者等待回执期间不持对象锁。这份宿主关闭派发与插件新建异步工作分别计数:前者是已登记步骤的执行位置,后者在销毁阶段禁止。
跨线程依赖清理还要满足提供者的线程契约。Draining可先将资源交回B所属线程,等待回执后清空A对应字段,原依赖边仍保留到A析构结束。确需在析构内同步跨线程调用时,准备合法封送接口或宿主清理代理,由B所属线程执行;以“等待线程→服务线程”建立等待图,要求无环且服务消息循环存活。有环就把相关结算拆到Draining完成,再进入销毁。本例B的缓存归还接口允许在A所在的T调用,锁保护缓存数据,依赖边保护其寿命。
T或其Apartment未能处理清理消息时,记录保持Queued或Invoking,保留对象、模块和依赖。超时不能把Destroy搬到任意工作线程重试。最后一个相关对象及关闭跳板退出后,才撤销清理通道、结束对应COM初始化并允许T退出;插件代码卸载还要满足 插件关闭与卸载的模块条件。
T完成显式收尾后,H等待收尾回执和同步回调退出。H共同检查各对象收尾完成、模块既有调用与后台活动为零、本阶段清理计数归零,才进入Destroying。A、B仍在对象表,A→B仍保留。尚未授权的Destroy槽只是预留记录,不计为正在执行的任务,因此不会形成“必须先析构才能排空、必须先排空才能析构”的循环。
实现与证据
本文说明机制与宿主协议,沿A、B、T手工推演。关闭步骤、协调者代次、结果收件表及清理授权均为示例宿主协议。尚未编译动态库、加载宿主或执行实际COM异步撤销;编译、运行与性能结果分别验收。
相关主题
参考资料
- Microsoft:引用计数规则、进程、线程与Apartment:2026-10-23核读接口引用的获取释放与跨Apartment传递。
- Microsoft单线程Apartment、DestroyWindow:2026-10-24核读所属线程、跨Apartment封送、消息循环及窗口销毁线程限制。协调者、步骤执行权和关闭通道是本文定义的宿主协议。
- Microsoft Dynamic-Link Libraries:模块加载与导出边界。
异步尝试、收件表、清理授权与接管规则由正文协议定义,API文档只支持各自的引用和线程语义。