Вызов асинхронных методов

В Media Foundation многие операции выполняются асинхронно. Асинхронные операции могут повысить производительность, так как они не блокируют вызывающий поток. Асинхронная операция в Media Foundation определяется парой методов с соглашением об именовании Begin... и End..... Вместе эти два метода определяют асинхронную операцию чтения в потоке.

Чтобы запустить асинхронную операцию, вызовите метод Begin . Метод Begin всегда содержит по крайней мере два параметра:

  • Указатель на интерфейс IMFAsyncCallback . Приложение должно реализовать этот интерфейс.
  • Указатель на необязательный объект состояния.

Интерфейс IMFAsyncCallback уведомляет приложение о завершении операции. Пример реализации этого интерфейса см. в разделе Реализация асинхронного обратного вызова. Объект состояния необязателен. Если он указан, он должен реализовывать интерфейс IUnknown . Объект состояния можно использовать для хранения любых сведений о состоянии, необходимых для обратного вызова. Если сведения о состоянии не требуются, можно задать для этого параметра значение NULL. Метод Begin может содержать дополнительные параметры, относящиеся к конкретному методу.

Если метод Begin возвращает код сбоя, это означает, что операция завершилась сбоем немедленно, и обратный вызов не будет вызван. Если метод Begin завершается успешно, это означает, что операция находится в состоянии ожидания, а обратный вызов будет вызван после завершения операции.

Например, метод IMFByteStream::BeginRead запускает асинхронную операцию чтения в потоке байтов:

    BYTE data[DATA_SIZE];
    ULONG cbRead = 0;

    CMyCallback *pCB = new (std::nothrow) CMyCallback(pStream, &hr);

    if (pCB == NULL)
    {
        hr = E_OUTOFMEMORY;
    }

    // Start an asynchronous request to read data.
    if (SUCCEEDED(hr))
    {
        hr = pStream->BeginRead(data, DATA_SIZE, pCB, NULL);
    }

Метод обратного вызова — IMFAsyncCallback::Invoke. Он имеет один параметр pAsyncResult, который является указателем на интерфейс IMFAsyncResult . Чтобы завершить асинхронную операцию, вызовите метод End , соответствующий асинхронным методу Begin . Метод End всегда принимает указатель IMFAsyncResult . Всегда передайте тот же указатель, который вы получили в методе Invoke . Асинхронная операция не будет завершена, пока вы не вызовете метод End . Метод End можно вызвать из метода Invoke или из другого потока.

Например, чтобы завершить IMFByteStream::BeginRead в потоке байтов, вызовите IMFByteStream::EndRead:

    STDMETHODIMP Invoke(IMFAsyncResult* pResult)
    {
        m_hrStatus = m_pStream->EndRead(pResult, &m_cbRead);

        SetEvent(m_hEvent);

        return S_OK;
    }

Возвращаемое значение метода End указывает состояние асинхронной операции. В большинстве случаев приложение выполняет определенную работу после завершения асинхронной операции, независимо от того, успешно ли она выполнена или нет. Доступно несколько параметров.

  • Выполните работу в методе Invoke . Такой подход допустим, если приложение никогда не блокирует вызывающий поток или не ожидает чего-либо в методе Invoke . Если вы заблокируйте вызывающий поток, вы можете заблокировать конвейер потоковой передачи. Например, можно обновить некоторые переменные состояния, но не выполнять синхронную операцию чтения или ждать объекта мьютекса.
  • В методе Invoke задайте событие и немедленно вернитесь. Вызывающий поток приложения ожидает события и выполняет работу при сигнале о событии.
  • В методе Invoke опубликуйте сообщение закрытого окна в окне приложения и немедленно вернитесь. Приложение выполняет работу со своим циклом сообщений. (Обязательно опубликуйте сообщение, вызвав PostMessage, а не SendMessage, так как SendMessage может заблокировать поток обратного вызова.)

Важно помнить, что invoke вызывается из другого потока. Ваше приложение отвечает за то, чтобы все операции, выполняемые внутри Invoke , были потокобезопасны.

По умолчанию поток, вызывающий Invoke , не делает никаких предположений о поведении объекта обратного вызова. При необходимости можно реализовать метод IMFAsyncCallback::GetParameters , чтобы получить сведения о реализации обратного вызова. В противном случае этот метод может возвращать E_NOTIMPL:

    STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        // Implementation of this method is optional.
        return E_NOTIMPL;
    }

Если вы указали объект состояния в методе Begin , его можно получить из метода обратного вызова, вызвав IMFAsyncResult::GetState.

Асинхронные методы обратного вызова