IAsyncResult.AsyncWaitHandle.WaitOne() se termine à l'avance de rappel

Voici le code:

class LongOp
{
    //The delegate
    Action longOpDelegate = LongOp.DoLongOp;
    //The result
    string longOpResult = null;

    //The Main Method
    public string CallLongOp()
    {
        //Call the asynchronous operation
        IAsyncResult result = longOpDelegate.BeginInvoke(Callback, null);

        //Wait for it to complete
        result.AsyncWaitHandle.WaitOne();

        //return result saved in Callback
        return longOpResult;
    }

    //The long operation
    static void DoLongOp()
    {
        Thread.Sleep(5000);
    }

    //The Callback
    void Callback(IAsyncResult result)
    {
        longOpResult = "Completed";
        this.longOpDelegate.EndInvoke(result);
    }
}

Ici, c'est le cas de test:

[TestMethod]
public void TestBeginInvoke()
{
    var longOp = new LongOp();
    var result = longOp.CallLongOp();

    //This can fail
    Assert.IsNotNull(result);
}

Si c'est exécuter les cas de test peut échouer. Pourquoi exactement?

Il y a très peu de documentation sur la façon de déléguer.BeginInvoke œuvres. Quelqu'un aurait-il des idées qu'ils aimeraient partager?

Mise à jour
C'est un subtil course-condition qui n'est pas bien documentée dans MSDN ou ailleurs. Le problème, comme expliqué dans la accepté de répondre, c'est que lorsque l'opération est terminée l'Attente de la Poignée est signalée, et puis le Rappel est exécuté. Le signal communiqués de l'attente thread principal et maintenant le Rappel d'exécution entre dans la "course". Jeffry de Richter a suggéré la mise en œuvre montre ce qui se passe derrière les coulisses:

  //If the event exists, set it   
  if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();

  //If a callback method was set, call it  
  if (m_AsyncCallback != null) m_AsyncCallback(this);

Pour une solution reportez-vous à Ben Voigt réponse. Que la mise en œuvre n'a pas à assumer la charge supplémentaire d'un deuxième attendre poignée.

Supprimer le rappel, et essayez à nouveau.
si vous remarquez que la question n'est pas de se demander "Comment puis-je obtenir que cela fonctionne?" Clairement, c'est un exemple artificiel.
Votre question est: "Si c'est exécuter les cas de test peut échouer. Pourquoi exactement?". J' répondre. Parce que vous essayer de mélanger deux façons très différentes de traitement d'une opération asynchrone.
Il dépend du Sommeil (argument)? Je ne suis pas l'achat. Post repro que n'importe qui peut essayer de l'exécuter sur leur machine sans un test runner.
vous pouvez juste essayer var longOp = new LongOp(); partout où vous le souhaitez. Mais vous devez savoir que?!

OriginalL'auteur Noel Abrahams | 2010-11-04