Использование рабочих очередей

В этом разделе описывается использование рабочих очередей в 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 автоматически блокирует платформу Media Foundation при создании результирующих объектов. Освобождение интерфейса разблокирует платформу. Поэтому вам почти никогда не придется напрямую блокировать платформу. Но если вы напишете собственную пользовательскую реализацию IMFAsyncResult, вам следует вручную заблокировать и разблокировать платформу. Чтобы заблокировать платформу, вызовите MFLockPlatform. Чтобы разблокировать платформу, вызовите MFUnlockPlatform. Пример см. в разделе Пользовательские асинхронные результируемые объекты.

После вызова MFShutdown необходимо убедиться, что платформа разблокирована в течение 5-секундного периода ожидания. Для этого отпустите все указатели IMFAsyncResult и вызовите MFUnlockPlatform , если вы заблокировали платформу вручную. Не забудьте освободить все ресурсы, используемые потоками рабочих очередей, иначе в приложении может возникнуть утечка памяти.

Как правило, если приложение завершает работу и освобождает все объекты Media Foundation перед вызовом MFShutdown, вам не нужно беспокоиться о блокировке. Механизм блокировки просто позволяет потокам рабочей очереди корректно завершать работу после вызова MFShutdown.

Использование запланированных рабочих элементов

Вы можете запланировать обратный вызов через заданный период времени, вызвав MFScheduleWorkItem или MFScheduleWorkItemEx.

  • MFScheduleWorkItem принимает указатель на обратный вызов, необязательный объект состояния и интервал времени ожидания.
  • MFScheduleWorkItemEx принимает указатель на асинхронный результирующий объект и значение времени ожидания.

Укажите время ожидания как отрицательное значение в миллисекундах. Например, чтобы запланировать обратный вызов через 5 секунд, используйте значение -5000. Обе функции возвращают значение MFWORKITEM_KEY , которое можно использовать для отмены обратного вызова, передав его в функцию MFCancelWorkItem .

Запланированные рабочие элементы всегда используют рабочую очередь платформы MFASYNC_CALLBACK_QUEUE_TIMER.

Использование периодических обратных вызовов

Функция MFAddPeriodicCallback планирует периодически вызывать обратный вызов, пока вы не отмените его. Интервал обратного вызова фиксированный; приложения не могут изменить его. Чтобы узнать точный интервал, вызовите MFGetTimerPeriodicity. Интервал составляет порядка 10 миллисекундах, поэтому эта функция предназначена для ситуаций, когда требуется частое "деление", например, реализация часов представления. Если вы хотите запланировать выполнение операции реже, используйте запланированный рабочий элемент, как описано выше.

В отличие от других обратных вызовов, описанных в этом разделе, периодический обратный вызов не использует интерфейс IMFAsyncCallback . Вместо этого используется указатель функции. Дополнительные сведения см. в разделе Обратный вызов MFPERIODICCALLBACK.

Чтобы отменить периодический обратный вызов, вызовите MFRemovePeriodicCallback.

Периодические обратные вызовы используют рабочую очередь платформы MFASYNC_CALLBACK_QUEUE_TIMER.

Рабочие очереди

MFASYNCRESULT

Улучшения рабочих очередей и потоков