调用异步方法

在 Media Foundation 中,许多操作都是异步执行的。 异步操作可以提高性能,因为它们不会阻止调用线程。 Media Foundation 中的异步操作由命名约定 Begin...End....的一对方法定义。 这两种方法共同定义流上的异步读取操作。

若要启动异步操作,请调用 Begin 方法。 Begin 方法始终至少包含两个参数:

  • 指向 IMFAsyncCallback 接口的指针。 应用程序必须实现此接口。
  • 指向可选状态对象的指针。

当操作完成时, IMFAsyncCallback 接口会通知应用程序。 有关如何实现此接口的示例,请参阅 实现异步回调。 状态对象是可选的。 如果指定,它必须实现 IUnknown 接口。 可以使用状态对象来存储回调所需的任何状态信息。 如果不需要状态信息,可以将此参数设置为 NULLBegin 方法可能包含特定于该方法的其他参数。

如果 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 接口的指针。 若要完成异步操作,请调用与异步 Begin 方法匹配的 End 方法。 End 方法始终采用 IMFAsyncResult 指针。 始终传入在 Invoke 方法中收到的同一指针。 在调用 End 方法之前,异步操作不会完成。 可以从 Invoke 方法内部调用 End 方法,也可以从另一个线程调用它。

例如,若要在字节流上完成 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 从回调方法内部检索它。

异步回调方法