Llamar a métodos asincrónicos

En Media Foundation, muchas operaciones se realizan de forma asincrónica. Las operaciones asincrónicas pueden mejorar el rendimiento, ya que no bloquean el subproceso que realiza la llamada. Una operación asincrónica en Media Foundation se define mediante un par de métodos con la convención de nomenclatura Begin... y End..... Juntos, estos dos métodos definen una operación de lectura asincrónica en la secuencia.

Para iniciar una operación asincrónica, llame al método Begin . El método Begin siempre contiene al menos dos parámetros:

  • Puntero a la interfaz IMFAsyncCallback . La aplicación debe implementar esta interfaz.
  • Puntero a un objeto de estado opcional.

La interfaz IMFAsyncCallback notifica a la aplicación cuando se completa la operación. Para obtener un ejemplo de cómo implementar esta interfaz, consulte Implementación de la devolución de llamada asincrónica. El objeto de estado es opcional. Si se especifica, debe implementar la interfaz IUnknown . Puede usar el objeto de estado para almacenar cualquier información de estado que necesite la devolución de llamada. Si no necesita información de estado, puede establecer este parámetro en NULL. El método Begin puede contener parámetros adicionales específicos de ese método.

Si el método Begin devuelve un código de error, significa que se ha producido un error en la operación inmediatamente y no se invocará la devolución de llamada. Si el método Begin se realiza correctamente, significa que la operación está pendiente y la devolución de llamada se invocará cuando se complete la operación.

Por ejemplo, el método IMFByteStream::BeginRead inicia una operación de lectura asincrónica en una secuencia de bytes:

    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);
    }

El método de devolución de llamada es IMFAsyncCallback::Invoke. Tiene un único parámetro, pAsyncResult, que es un puntero a la interfaz IMFAsyncResult . Para completar la operación asincrónica, llame al método End que coincida con el método Begin asincrónico. El método End siempre toma un puntero IMFAsyncResult . Pase siempre el mismo puntero que recibió en el método Invoke . La operación asincrónica no se completa hasta que se llama al método End . Puede llamar al método End desde dentro del método Invoke o puede llamarlo desde otro subproceso.

Por ejemplo, para completar imfByteStream::BeginRead en una secuencia de bytes, llame a IMFByteStream::EndRead:

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

        SetEvent(m_hEvent);

        return S_OK;
    }

El valor devuelto del método End indica el estado de la operación asincrónica. En la mayoría de los casos, la aplicación realizará algún trabajo una vez completada la operación asincrónica, tanto si se completa correctamente como si no. Tiene varias opciones:

  • Realice el trabajo dentro del método Invoke . Este enfoque es aceptable siempre que la aplicación nunca bloquee el subproceso que realiza la llamada ni espere nada dentro del método Invoke . Si bloquea el subproceso que llama, es posible que bloquee la canalización de streaming. Por ejemplo, puede actualizar algunas variables de estado, pero no realizar una operación de lectura sincrónica ni esperar en un objeto de exclusión mutua.
  • En el método Invoke , establezca un evento y devuelva inmediatamente. El subproceso que realiza la llamada de la aplicación espera el evento y realiza el trabajo cuando se señala el evento.
  • En el método Invoke , publique un mensaje de ventana privada en la ventana de la aplicación y vuelva inmediatamente. La aplicación realiza el trabajo en su bucle de mensajes. (Asegúrese de publicar el mensaje llamando a PostMessage, no SendMessage, porque SendMessage puede bloquear el subproceso de devolución de llamada).

Es importante recordar que se llama a Invoke desde otro subproceso. La aplicación es responsable de garantizar que cualquier trabajo realizado dentro de Invoke sea seguro para subprocesos.

De forma predeterminada, el subproceso que llama a Invoke no supone el comportamiento del objeto de devolución de llamada. Opcionalmente, puede implementar el método IMFAsyncCallback::GetParameters para devolver información sobre la implementación de devolución de llamada. De lo contrario, este método puede devolver E_NOTIMPL:

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

Si especificó un objeto de estado en el método Begin , puede recuperarlo desde dentro del método de devolución de llamada llamando a IMFAsyncResult::GetState.

Métodos de devolución de llamada asincrónica