使用工作队列

本主题介绍如何在 Microsoft Media Foundation 中使用工作队列。

使用工作队列

工作队列是在另一个线程上执行异步操作的有效方法。 从概念上讲,你将工作项放在队列中,队列有一个线程,该线程从队列中拉取每个项并对其进行调度。 工作项通过使用 IMFAsyncCallback 接口实现为回调。

Media Foundation 创建多个标准工作队列,称为 平台工作队列。 应用程序还可以创建自己的工作队列,称为 专用工作队列。 有关平台工作队列的列表,请参阅 工作队列标识符。 若要创建专用工作队列,请调用 MFAllocateWorkQueue。 此函数返回新工作队列的标识符。 若要将项放入队列中,请调用 MFPutWorkItemMFPutWorkItemEx。 在这两种情况下,都必须指定回调接口。

  • MFPutWorkItem 采用指向 IMFAsyncCallback 接口的指针,以及实现 IUnknown 的可选状态对象。 这些参数与异步方法中使用的参数相同,如异步 回调方法主题中所述。 在内部,此函数创建一个异步结果对象,该对象将传递给回调的 Invoke 方法。
  • MFPutWorkItemEx 采用指向 IMFAsyncResult 接口的指针。 此接口表示异步结果对象。 通过调用 MFCreateAsyncResult 并指定回调接口和状态对象(可选)来创建此对象。

在这两种情况下,工作队列线程都会调用 IMFAsyncCallback::Invoke 方法。 使用 Invoke 方法执行工作项。

如果多个线程或组件共享同一个工作队列,则可以调用 MFLockWorkQueue 来锁定工作队列,从而阻止平台释放工作队列。 对于每次调用 MFAllocateWorkQueueMFLockWorkQueue,必须调用 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,将回调安排在设定的时间段后发生。

将超时指定为负值(以毫秒为单位)。 例如,若要计划要在 5 秒内调用的回调,请使用值 •5000。 这两个函数都返回 一个MFWORKITEM_KEY 值,可以使用该值通过将回调传递给 MFCancelWorkItem 函数来取消回调。

计划的工作项始终使用MFASYNC_CALLBACK_QUEUE_TIMER平台工作队列。

使用定期回调

MFAddPeriodicCallback 函数计划定期调用回调,直到取消回调。 回调间隔是固定的;应用程序无法更改它。 若要找出确切的间隔,请调用 MFGetTimerPeriodicity。 间隔为 10 毫秒,因此此函数适用于需要频繁“滴答”的情况,例如实现演示时钟。 如果希望将操作计划为减少发生频率,请使用计划的工作项,如前所述。

与本主题中所述的其他回调不同,定期回调不使用 IMFAsyncCallback 接口。 而是使用函数指针。 有关详细信息,请参阅 MFPERIODICCALLBACK 回调

若要取消定期回调,请调用 MFRemovePeriodicCallback

定期回调使用MFASYNC_CALLBACK_QUEUE_TIMER平台工作队列。

工作队列

MFASYNCRESULT

工作队列和线程处理改进