本主题介绍 Windows 8 中工作队列和Microsoft Media Foundation 平台中的线程处理改进。
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 任务创建一个多线程工作队列。 媒体基础平台根据需要为每个 MMCSS 任务分配这些资源,每个进程最多分配一个。 若要获取特定 MMCSS 任务的共享工作队列,请调用 MFLockSharedWorkQueue 并指定任务名称。 该函数在表中查找任务名称。 如果此任务尚不存在工作队列,该函数会分配新的 MT 工作队列,并立即将其加入 MMCSS 任务。 如果该任务已存在工作队列,该函数将返回现有工作队列的标识符。
等待队列
等待队列 是一个特殊的平台工作队列,等待事件发出信号。 如果组件需要等待事件发出信号,则可以使用等待队列,而不是创建工作线程来等待事件。
若要使用等待队列,请调用 MFPutWaitingWorkItem。 参数包括事件句柄和 IMFAsyncResult 指针。 发出事件信号后,等待队列将调用回调。 有一个平台等待队列;应用程序无法创建自己的等待队列。
MMCSS 支持的增强功能
以下新的媒体基础平台函数与 MMCSS 相关。
功能 | 描述 |
---|---|
MFBeginRegisterWorkQueueWithMMCSSEx | 向 MMCSS 注册工作队列。 此函数包含用于指定相对线程优先级的参数。 在内部,此值转换为调用 AvSetMmThreadPriority。 |
MFGetWorkQueueMMCSSPriority | 查询工作队列的优先级。 |
MFRegisterPlatformWithMMCSS | 向 MMCSS 任务注册所有平台工作队列。 此函数类似于 IMFWorkQueueServices::BeginRegisterPlatformWorkQueueWithMMCSS 方法,但可以在不创建媒体会话实例的情况下使用它。 此外,该函数还包含用于指定基线程优先级的参数。 |
使用媒体会话的应用程序应将音频呈现分支的 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 属性设置为“Audio”。 将视频呈现分支的属性设置为“播放”。
IMFRealTimeClientEx
IMFRealTimeClientEx 接口,用于替换执行异步作的管道组件的 IMFRealTimeClient。
方法 | 描述 |
---|---|
RegisterThreadsEx | 通知组件将其线程注册到 MMCSS。 此方法等效于 IMFRealTimeClient::RegisterThreads,但它为基线程优先级添加参数。 |
SetWorkQueueEx | 通知组件使用特定的工作队列。 此方法等效于 IMFReadTimeClient::SetWorkQueue,但它为工作项优先级添加了参数。 |
UnregisterThreads | 通知组件从 MMCSS 注销其线程。 此方法与 IMFRealTimeClient::UnregisterThreads 方法相同。 |
管道组件应使用工作队列,不应创建工作线程,原因如下:
- 工作队列可以更好地缩放,因为它们使用 OS 线程池。
- 平台处理使用 MMCSS 注册工作队列的详细信息。
- 工作线程很容易导致难以调试的死锁。
此外,如果需要序列化异步作,请考虑使用序列化程序工作队列。
拓扑分支
如果 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 属性向 MMCSS 注册拓扑分支,则 Windows 8 中的媒体会话使用共享 MT 工作队列。 在早期版本的 Windows 中,媒体会话分配了新的工作队列。
定义了两个新属性,用于向 MMCSS 注册拓扑分支。
属性 | 描述 |
---|---|
MF_TOPONODE_WORKQUEUE_MMCSS_PRIORITY | 指定基线程优先级。 |
MF_TOPONODE_WORKQUEUE_ITEM_PRIORITY | 指定工作项优先级。 |
建议
- 使用媒体会话的应用程序应将音频呈现分支 MF_TOPONODE_WORKQUEUE_MMCSS_CLASS 设置为“音频”,并将视频呈现分支的“播放”设置为“播放”。
- 使用媒体会话的应用程序应在拓扑上调用 IMFWorkQueueServices::BeginRegisterTopologyWorkQueuesWithMMCSS。
- 对于管道组件,建议使用工作队列,而不是工作线程。 如果组件使用工作队列或工作线程,请实现 IMFRealTimeClientEx。
- 不要创建专用工作队列,因为这样会破坏平台工作队列的目的。 使用平台多线程队列或包装平台多线程队列的串行队列。
- 如果需要序列化异步作,请使用串行队列。
总结
以下与线程和工作队列相关的媒体基础平台 API 是 Windows 8 的新增功能。
- MF_TOPONODE_WORKQUEUE_ITEM_PRIORITY
- MF_TOPONODE_WORKQUEUE_MMCSS_PRIORITY
- MFAllocateSerialWorkQueue
- MFBeginRegisterWorkQueueWithMMCSSEx
- MFGetWorkQueueMMCSSPriority
- MFPutWaitingWorkItem
- MFPutWorkItem2
- MFPutWorkItemEx2
- MFRegisterPlatformWithMMCSS
- MFUnregisterPlatformFromMMCSS
- MFLockSharedWorkQueue
- IMFRealTimeClientEx
相关主题