使用工作队列
本主题介绍如何在 Microsoft Media Foundation 中使用工作队列。
使用工作队列
工作队列是在另一个线程上执行异步操作的有效方法。 从概念上讲,你将工作项放在队列中,队列有一个线程,该线程从队列中拉取每个项并对其进行调度。 工作项通过使用 IMFAsyncCallback 接口实现为回调。
Media Foundation 创建多个标准工作队列,称为 平台工作队列。 应用程序还可以创建自己的工作队列,称为 专用工作队列。 有关平台工作队列的列表,请参阅 工作队列标识符。 若要创建专用工作队列,请调用 MFAllocateWorkQueue。 此函数返回新工作队列的标识符。 若要将项放入队列中,请调用 MFPutWorkItem 或 MFPutWorkItemEx。 在这两种情况下,都必须指定回调接口。
- MFPutWorkItem 采用指向 IMFAsyncCallback 接口的指针,以及实现 IUnknown 的可选状态对象。 这些参数与异步方法中使用的参数相同,如异步 回调方法主题中所述。 在内部,此函数创建一个异步结果对象,该对象将传递给回调的 Invoke 方法。
- MFPutWorkItemEx 采用指向 IMFAsyncResult 接口的指针。 此接口表示异步结果对象。 通过调用 MFCreateAsyncResult 并指定回调接口和状态对象(可选)来创建此对象。
在这两种情况下,工作队列线程都会调用 IMFAsyncCallback::Invoke 方法。 使用 Invoke 方法执行工作项。
如果多个线程或组件共享同一个工作队列,则可以调用 MFLockWorkQueue 来锁定工作队列,从而阻止平台释放工作队列。 对于每次调用 MFAllocateWorkQueue 或 MFLockWorkQueue,必须调用 MFUnlockWorkQueue 一次才能释放工作队列。 例如,如果创建新的工作队列,然后将其锁定一次,则必须调用 MFUnlockWorkQueue 两次,一次用于调用 MFAllocateWorkQueue ,一次用于调用 MFLockWorkQueue。
以下代码演示如何创建新的工作队列、将工作项放入队列以及释放工作队列。
有关Windows 8中工作队列的其他信息,请参阅工作队列和线程改进。
DWORD idWorkQueue = 0;
HRESULT hr = S_OK;
// Create a new work queue.
hr = MFAllocateWorkQueue(&idWorkQueue);
// Put an item on the queue.
if (SUCCEEDED(hr))
{
hr = MFPutWorkItem(idWorkQueue, pCallback, NULL);
}
// Wait for the callback to be invoked.
if (SUCCEEDED(hr))
{
WaitForSingleObject(hEvent, INFINITE);
}
// Release the work queue.
if (SUCCEEDED(hr))
{
hr = MFUnlockWorkQueue(idWorkQueue);
}
此示例假定 pCallback 是指向应用程序 IMFAsyncCallback 接口的指针。 它还假定回调设置 hEvent 事件句柄。 代码在调用 MFUnlockWorkQueue 之前等待设置此事件。
工作队列线程始终在调用方的进程中创建。 在每个工作队列中,将序列化回调。 如果使用相同的工作队列调用 MFPutWorkItem 两次,则在返回第一个回调之前,不会调用第二个回调。
关闭工作队列
在调用 MFShutdown 之前,请释放工作队列线程正在使用的任何资源。 若要同步此过程,可以锁定 Media Foundation 平台,这会阻止 MFShutdown 函数关闭任何工作队列线程。 如果在锁定平台时调用 MFShutdown , 则 MFShutdown 将等待几百毫秒才能解锁平台。 如果在此时间内未解锁, MFShutdown 将关闭工作队列线程。
创建结果对象时, IMFAsyncResult 的默认实现会自动锁定媒体基础平台。 释放接口会解锁平台。 因此,几乎永远不需要直接锁定平台。 但是,如果编写自己的 IMFAsyncResult 自定义实现,则应手动锁定和解锁平台。 若要锁定平台,请调用 MFLockPlatform。 若要解锁平台,请调用 MFUnlockPlatform。 有关示例,请参阅 自定义异步结果对象。
调用 MFShutdown 后,需要确保平台在 5 秒超时期限内解锁。 为此,请释放所有 IMFAsyncResult 指针,如果手动锁定平台,则调用 MFUnlockPlatform 。 请确保释放工作队列线程正在使用的任何资源,否则应用程序可能会泄漏内存。
通常,如果应用程序在调用 MFShutdown 之前关闭并释放每个 Media Foundation 对象,则不必担心锁定问题。 锁定机制仅允许在调用 MFShutdown 后正常退出工作队列线程。
使用计划的工作项
可以通过调用 MFScheduleWorkItem 或 MFScheduleWorkItemEx,将回调安排在设定的时间段后发生。
- MFScheduleWorkItem 获取指向回调的指针、可选状态对象和超时间隔。
- MFScheduleWorkItemEx 获取指向异步结果对象的指针和超时值。
将超时指定为负值(以毫秒为单位)。 例如,若要计划要在 5 秒内调用的回调,请使用值 •5000。 这两个函数都返回 一个MFWORKITEM_KEY 值,可以使用该值通过将回调传递给 MFCancelWorkItem 函数来取消回调。
计划的工作项始终使用MFASYNC_CALLBACK_QUEUE_TIMER平台工作队列。
使用定期回调
MFAddPeriodicCallback 函数计划定期调用回调,直到取消回调。 回调间隔是固定的;应用程序无法更改它。 若要找出确切的间隔,请调用 MFGetTimerPeriodicity。 间隔为 10 毫秒,因此此函数适用于需要频繁“滴答”的情况,例如实现演示时钟。 如果希望将操作计划为减少发生频率,请使用计划的工作项,如前所述。
与本主题中所述的其他回调不同,定期回调不使用 IMFAsyncCallback 接口。 而是使用函数指针。 有关详细信息,请参阅 MFPERIODICCALLBACK 回调。
若要取消定期回调,请调用 MFRemovePeriodicCallback。
定期回调使用MFASYNC_CALLBACK_QUEUE_TIMER平台工作队列。
相关主题