工作队列和线程处理改进
本主题介绍 Microsoft Media Foundation 平台中工作队列和线程Windows 8的改进。
Windows 7 行为
本部分汇总了 Windows 7 中 Media Foundation 工作队列的行为。
工作队列
Media Foundation 平台创建多个标准工作队列。 只有两个记录为常规应用程序用途:
- MFASYNC_CALLBACK_QUEUE_STANDARD
- MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION
应用程序或组件可以通过调用 MFAllocateWorkQueue 或 MFAllocateWorkQueueEx 来分配新的工作队列。 MFAllocateWorkQueueEx 函数定义两种类型的工作队列:
- MF_STANDARD_WORKQUEUE 创建没有消息循环的工作队列。
- MF_WINDOW_WORKQUEUE 创建具有消息循环的工作队列。
若要将工作项排队,请调用 MFPutWorkItem 或 MFPutWorkItemEx。 平台通过调用调用方提供的 IMFAsyncCallback 实现来执行工作项。 在 Windows 7 及更早版本中,平台为每个工作队列创建一个线程。
MMCSS 支持
多媒体类计划程序服务 (MMCSS) 管理线程优先级,以便多媒体应用程序获得常规的 CPU 时间切片,而不会拒绝将 CPU 资源用于低优先级应用程序。 MMCSS 定义了一组具有不同 CPU 使用率配置文件 的任务 。 当线程加入 MMCSS 任务时,MMCSS 根据几个因素设置线程的优先级:
- 任务的基本优先级,在注册表中设置。
- 相对线程优先级,通过调用 AvSetMmThreadPriority 在运行时设置。
- 各种运行时特征,例如应用程序是否在前台,以及每个 MMCSS 类中的线程占用的 CPU 时间。
应用程序可以通过调用 MFBeginRegisterWorkQueueWithMMCSS 向 MMCSS 注册工作队列。 此函数采用工作队列 ID、MMCSS 类 (任务名称) 和 MMCSS 任务标识符。 在内部,它使用任务名称和任务 ID 调用 AvSetMmThreadCharacteristics 。 向 MMCSS 注册工作队列后,可以通过调用 MFGetWorkQueueMMCSSClass 和 MFGetWorkQueueMMCSSTaskId 来获取类和任务 ID。
媒体会话通过 IMFWorkQueueServices 接口提供对这些 API 的更高级别的访问。 此接口提供两种主要方法:
方法 | 说明 |
---|---|
BeginRegisterPlatformWorkQueueWithMMCSS | 向 MMCSS 任务注册工作队列。 此方法实质上是 围绕 MFBeginRegisterWorkQueueWithMMCSS 的精简包装器,但你可以 传递值MFASYNC_CALLBACK_QUEUE_ALL 一次注册所有平台工作队列。 |
BeginRegisterTopologyWorkQueuesWithMMCSS | 将拓扑的分支注册到工作队列。 |
若要注册拓扑分支,请执行以下操作。
- 在分支的源节点上设置 MF_TOPONODE_WORKQUEUE_ID 属性。 使用任何应用程序定义的值。
- (可选)设置 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 以将工作队列加入 MMCSS 任务。
- 在已解析的拓扑上调用 BeginRegisterTopologyWorkQueuesWithMMCSS 。
媒体会话为每个唯一值 MF_TOPONODE_WORKQUEUE_ID分配一个新工作队列。 对于每个拓扑分支,对分配给分支的工作队列执行异步管道操作。
IMFRealTimeClient
IMFRealTimeClient 接口适用于创建自己的线程或使用工作队列执行异步操作的管道组件。 媒体会话使用此接口通知管道组件正确行为,如下所示:
- 如果管道组件创建工作线程, 则 IMFRealTimeClient::RegisterThreads 方法会通知组件要加入哪个 MMCSS 类。
- 如果管道组件使用工作队列, 则 IMFRealTimeClient::SetWorkQueue 方法会告知组件要使用哪个工作队列。
通常,管道组件使用线程或工作队列来执行异步任务,但不能同时使用两者。
Windows 8改进
多线程工作队列
在 Windows 8 中,Media Foundation 支持一种称为多线程队列的新型工作队列。 多线程队列使用系统线程池来调度工作项。 与以前的单线程队列相比,多线程队列的缩放效果更好。 例如,
多个组件可以共享多线程队列,而不会相互阻塞,因此需要创建更少的线程。
如果已设置事件,则对工作项进行优化,以避免上下文切换。 这比创建自己的线程来等待事件更高效。
使用 IMFRealTimeClientEx 时,应用程序应避免启动线程,而应使用工作队列。 为此,应用程序应实现 SetWorkQueueEx ,而不使用 RegisterThreads 和 UnregisterThreads。
初始化 Media Foundation 平台时,它会创建标识符 为 MFASYNC_CALLBACK_QUEUE_MULTITHREADED的多线程队列。
多线程队列不会序列化工作项。 每当线程池中的线程变得可用时,队列上的下一个工作项就会调度。 调用方必须确保工作已正确序列化。 为了简化此操作,Media Foundation 定义了 串行工作队列。 串行队列包装另一个工作队列,但保证完全序列化的执行。 在上一项完成之前,不会调度队列中的下一项。
以下代码通过平台多线程队列创建序列化程序队列。
DWORD workQueueID;
hr = MFAllocateSerialWorkQueue(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, &workQueueID);
多个串行队列可以包装同一个多线程队列。 然后,串行队列共享同一个线程池,并在每个队列中强制执行序列化执行。
Windows 8之前存在的标准工作队列现在实现为包装平台多线程队列的串行工作队列。 此更改保留了向后兼容性。
共享任务工作队列
若要正确使用内核计划程序,应为每个使用的 MMCSS 任务提供一个多线程工作队列。 Media Foundation 平台根据需要分配这些资源,每个 MMCSS 任务、每个进程最多分配一个。 若要获取特定 MMCSS 任务的共享工作队列,请调用 MFLockSharedWorkQueue 并指定任务名称。 函数在表中查找任务名称。 如果此任务尚不存在工作队列,函数会分配一个新的 MT 工作队列,并立即将其加入 MMCSS 任务。 如果已存在该任务的工作队列,则函数将返回现有工作队列的标识符。
等待队列
等待队列是一个特殊的平台工作队列,用于等待事件发出信号。 如果组件需要等待事件发出信号,则可以使用等待队列,而不是创建工作线程来等待事件。
若要使用等待队列,请调用 MFPutWaitingWorkItem。 参数包括事件句柄和 IMFAsyncResult 指针。 当事件发出信号时,等待队列将调用回调。 有一个平台等待队列;应用程序无法创建自己的等待队列。
MMCSS 支持的增强功能
以下新的 Media Foundation 平台函数与 MMCSS 相关。
函数 | 说明 |
---|---|
MFBeginRegisterWorkQueueWithMMCSSEx | 向 MMCSS 注册工作队列。 此函数包含用于指定相对线程优先级的参数。 在内部,此值转换为对 AvSetMmThreadPriority 的调用。 |
MFGetWorkQueueMMCSSPriority | 查询工作队列的优先级。 |
MFRegisterPlatformWithMMCSS | 向 MMCSS 任务注册所有平台工作队列。 此函数类似于 IMFWorkQueueServices::BeginRegisterPlatformWorkQueueWithMMCSS 方法,但可以在不创建媒体会话实例的情况下使用它。 此外,函数包括一个参数,用于指定基线程优先级。 |
使用媒体会话的应用程序应将音频呈现分支的 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 属性设置为“Audio”。 将视频呈现分支的 属性设置为“播放”。
IMFRealTimeClientEx
用于替换执行异步操作的管道组件的 IMFRealTimeClient 的 IMFRealTimeClientEx 接口。
方法 | 说明 |
---|---|
RegisterThreadsEx | 通知组件将其线程注册到 MMCSS。 此方法等效于 IMFRealTimeClient::RegisterThreads,但它为基线程优先级添加了参数。 |
SetWorkQueueEx | 通知组件使用特定工作队列。 此方法等效于 IMFReadTimeClient::SetWorkQueue,但它为工作项优先级添加了参数。 |
UnregisterThreads | 通知组件从 MMCSS 注销其线程。 此方法与 IMFRealTimeClient::UnregisterThreads 方法相同。 |
管道组件应使用工作队列,不应创建工作线程,原因如下:
- 工作队列可以更好地缩放,因为它们使用 OS 线程池。
- 平台处理向 MMCSS 注册工作队列的详细信息。
- 工作线程很容易导致难以调试的死锁。
此外,如果需要序列化异步操作,请考虑使用序列化程序工作队列。
拓扑分支
如果 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 属性向 MMCSS 注册拓扑分支,则Windows 8媒体会话使用共享的 MT 工作队列。 在早期版本的 Windows 中,媒体会话分配了新的工作队列。
定义了两个新属性,用于向 MMCSS 注册拓扑分支。
Attribute | 说明 |
---|---|
MF_TOPONODE_WORKQUEUE_MMCSS_PRIORITY | 指定基线程优先级。 |
MF_TOPONODE_WORKQUEUE_ITEM_PRIORITY | 指定工作项优先级。 |
建议
- 使用媒体会话的应用程序应将 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 设置为音频呈现分支的“音频”,将视频呈现分支设置为“播放”。
- 使用媒体会话的应用程序应在拓扑上调用 IMFWorkQueueServices::BeginRegisterTopologyWorkQueuesWithMMCSS 。
- 对于管道组件,建议使用工作队列而不是工作线程。 如果组件使用工作队列或工作线程,请实现 IMFRealTimeClientEx。
- 请勿创建专用工作队列,因为这样做会破坏平台工作队列的目的。 使用平台多线程队列或包装平台多线程队列的串行队列。
- 如果需要序列化异步操作,请使用串行队列。
总结
以下与线程和工作队列相关的 Media Foundation 平台 API 是Windows 8的新增功能。
- MF_TOPONODE_WORKQUEUE_ITEM_PRIORITY
- MF_TOPONODE_WORKQUEUE_MMCSS_PRIORITY
- MFAllocateSerialWorkQueue
- MFBeginRegisterWorkQueueWithMMCSSEx
- MFGetWorkQueueMMCSSPriority
- MFPutWaitingWorkItem
- MFPutWorkItem2
- MFPutWorkItemEx2
- MFRegisterPlatformWithMMCSS
- MFUnregisterPlatformFromMMCSS
- MFLockSharedWorkQueue
- IMFRealTimeClientEx
相关主题