Appel de méthodes asynchrones

Dans Media Foundation, de nombreuses opérations sont effectuées de manière asynchrone. Les opérations asynchrones peuvent améliorer les performances, car elles ne bloquent pas le thread appelant. Une opération asynchrone dans Media Foundation est définie par une paire de méthodes avec les conventions d’affectation de noms Begin... et End..... Ensemble, ces deux méthodes définissent une opération de lecture asynchrone sur le flux.

Pour démarrer une opération asynchrone, appelez la méthode Begin . La méthode Begin contient toujours au moins deux paramètres :

  • Pointeur vers l’interface IMFAsyncCallback . L’application doit implémenter cette interface.
  • Pointeur vers un objet d’état facultatif.

L’interface IMFAsyncCallback avertit l’application lorsque l’opération est terminée. Pour obtenir un exemple d’implémentation de cette interface, consultez Implémentation du rappel asynchrone. L’objet state est facultatif. Si elle est spécifiée, elle doit implémenter l’interface IUnknown . Vous pouvez utiliser l’objet state pour stocker toutes les informations d’état nécessaires à votre rappel. Si vous n’avez pas besoin d’informations d’état, vous pouvez définir ce paramètre sur NULL. La méthode Begin peut contenir des paramètres supplémentaires spécifiques à cette méthode.

Si la méthode Begin retourne un code d’échec, cela signifie que l’opération a échoué immédiatement et que le rappel ne sera pas appelé. Si la méthode Begin réussit, cela signifie que l’opération est en attente et que le rappel est appelé une fois l’opération terminée.

Par exemple, la méthode IMFByteStream::BeginRead démarre une opération de lecture asynchrone sur un flux d’octets :

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

La méthode de rappel est IMFAsyncCallback::Invoke. Il a un seul paramètre, pAsyncResult, qui est un pointeur vers l’interface IMFAsyncResult . Pour terminer l’opération asynchrone, appelez la méthode End qui correspond à la méthode Begin asynchrone. La méthode End accepte toujours un pointeur IMFAsyncResult . Transmettez toujours le même pointeur que celui que vous avez reçu dans la méthode Invoke . L’opération asynchrone n’est pas terminée tant que vous n’avez pas appelé la méthode End . Vous pouvez appeler la méthode End à partir de la méthode Invoke , ou vous pouvez l’appeler à partir d’un autre thread.

Par exemple, pour terminer imfByteStream::BeginRead sur un flux d’octets, appelez IMFByteStream::EndRead :

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

        SetEvent(m_hEvent);

        return S_OK;
    }

La valeur de retour de la méthode End indique la status de l’opération asynchrone. Dans la plupart des cas, votre application effectue un certain travail une fois l’opération asynchrone terminée, qu’elle se termine correctement ou non. Vous disposez de plusieurs options :

  • Effectuez le travail à l’intérieur de la méthode Invoke . Cette approche est acceptable tant que votre application ne bloque jamais le thread appelant ou n’attend rien à l’intérieur de la méthode Invoke . Si vous bloquez le thread appelant, vous pouvez bloquer le pipeline de streaming. Par exemple, vous pouvez mettre à jour certaines variables d’état, mais n’effectuez pas d’opération de lecture synchrone ou n’attendez pas sur un objet mutex.
  • Dans la méthode Invoke , définissez un événement et retournez immédiatement. Le thread appelant de l’application attend l’événement et effectue le travail lorsque l’événement est signalé.
  • Dans la méthode Invoke , publiez un message de fenêtre privée dans votre fenêtre d’application et retournez immédiatement. L’application effectue le travail sur sa boucle de message. (Veillez à publier le message en appelant PostMessage, et non SendMessage, car SendMessage peut bloquer le thread de rappel.)

Il est important de se rappeler qu’Invoke est appelé à partir d’un autre thread. Votre application est chargée de s’assurer que tout travail effectué dans Invoke est thread-safe.

Par défaut, le thread qui appelle Invoke ne fait aucune hypothèse sur le comportement de votre objet de rappel. Si vous le souhaitez, vous pouvez implémenter la méthode IMFAsyncCallback::GetParameters pour retourner des informations sur votre implémentation de rappel. Sinon, cette méthode peut retourner E_NOTIMPL :

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

Si vous avez spécifié un objet d’état dans la méthode Begin , vous pouvez le récupérer à l’intérieur de votre méthode de rappel en appelant IMFAsyncResult::GetState.

Méthodes de rappel asynchrones