2009-08-11 7 views
2

Disons que j'ai une interface exposée en tant que telle:C# Threading Mécanisme

interface IMyService 
{ 
    MyResult MyOperation(); 
} 

Cette opération est synchrone et retourne une valeur.

Mon interface implémentée doit effectuer les opérations suivantes:

  • Appel d'une méthode asynchrone
  • Attendez événement # 1
  • Attendez événement # 2

Cela est dû à un Objet COM tiers avec lequel je travaille.

Ce code ressemble aux suivantes

public MyResult MyOperation() 
{ 
    _myCOMObject.AsyncOperation(); 

    //Here I need to wait for both events to fire before returning 
} 

private void MyEvent1() 
{ 
    //My Event 1 is fired in this handler 
} 

private void MyEvent2() 
{ 
    //My Event 2 is fired in this handler 
} 

Mes deux événements peuvent se produire dans un ordre, il est tout à fait aléatoire.

Quel est le mécanisme d'enfilage approprié que je peux utiliser pour synchroniser ceci? J'utilisais ManualResetEvent avant de devoir attendre le second événement, et je n'ai pas vu un moyen facile de l'utiliser pour les deux événements. Ces 2 événements définissent des variables qui me permettent de créer la valeur de retour pour MyOperation().

Des idées sur une bonne implémentation pour cela? Je n'ai aucun contrôle sur la façon dont l'objet tiers est implémenté.

Répondre

4

Deux ManualResetEvent s devraient faire l'affaire pour vous. Il suffit de les initialiser à false avant d'appeler le _myCOMObject.AsyncOperation(). Comme ceci:

private ManualResetEvent event1; 
private ManualResetEvent event2; 

public MyResult MyOperation() 
{ 
    event1 = new ManualResetEvent(false); 
    event2 = new ManualResetEvent(false); 

    _myCOMObject.AsyncOperation(); 

    WaitHandle.WaitAll(new WaitHandle[] { event1, event2 }); 
} 

private void MyEvent1() 
{ 
    event1.Set(); 
} 

private void MyEvent2() 
{ 
    event2.Set(); 
} 

Modifier

Merci pour les commentaires. J'ai changé l'appel d'attente pour utiliser WaitAll

+0

Ceci est logique. Ma confusion a menti sur l'endroit où appeler Reset() sur ManualResetEvents muliple, et je devrai également verrouiller l'appel entier à MyOperation() de sorte que 2 discussions ne puissent pas appeler la fonction en même temps. – jonathanpeppers

+2

Bien que ce soit une solution parfaitement bien, j'utiliserais WaitAll (voir ci-dessous) au lieu de deux appels de blocage WaitOne, le WaitAll donne au système plus de contexte sur le blocage, et lui donne potentiellement la chance de mieux planifier. – meandmycode

+0

Merci pour les commentaires. J'ai mis à jour ma réponse. –

0

Je ne suis pas sûr d'avoir compris votre question, mais AutoResetEvent.WaitAll semble résoudre votre problème, si je l'ai bien compris. Il vous permet de définir plus d'un gestionnaire et il ne sera libéré que lorsque tous sont définis.

http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx

2

Mon exemple de mise en œuvre est la suivante:

namespace ConsoleApplication1 
{ 

    class Program 
    { 
     private static WaitHandle[] waitHandles; 
     private static event EventHandler Evt1; 
     private static event EventHandler Evt2; 

     static void Main(string[] args) 
     { 
      waitHandles = new WaitHandle[]{ 
       new ManualResetEvent(false), 
       new ManualResetEvent(false) 
      }; 

      Evt1 += new EventHandler(Program_Evt1); 
      Evt2 += new EventHandler(Program_Evt2); 

      OnEvt1(); 
      OnEvt2(); 

      WaitHandle.WaitAll(waitHandles); 

      Console.WriteLine("Finished"); 
      Console.ReadLine(); 
     } 

     static void Program_Evt2(object sender, EventArgs e) 
     { 
      Thread.Sleep(2000); 
      ((ManualResetEvent)waitHandles[0]).Set(); 
     } 

     static void Program_Evt1(object sender, EventArgs e) 
     { 
      ((ManualResetEvent)waitHandles[1]).Set(); 
     } 

     static void OnEvt1() 
     { 
      if (Evt1 != null) 
       Evt1(null, EventArgs.Empty); 
     } 

     static void OnEvt2() 
     { 
      if (Evt2 != null) 
       Evt2(null, EventArgs.Empty); 
     } 


    } 
} 

Je fais dormir aux fins de cet exemple et la fonctionnalité WaitAll

Cheers,

Andrew

PS un autre exemple serait d'utiliser AsyncCallback, exemple vraiment rapide et sale, mais vous donne plus de clés pour ouvrir la porte avec :-). J'espère que cela t'aides!!

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     private static WaitHandle[] waitHandles; 
     private static event EventHandler Evt1; 
     private static event EventHandler Evt2; 

     static void Main(string[] args) 
     { 
      waitHandles = new WaitHandle[]{ 
       new ManualResetEvent(false), 
       new ManualResetEvent(false) 
      }; 

      var callabck1 = new AsyncCallback(OnEvt1); 
      var callabck2 = new AsyncCallback(OnEvt2); 

      callabck1.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[0])); 
      callabck2.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[1])); 

      WaitHandle.WaitAll(waitHandles); 

      Console.WriteLine("Finished"); 
      Console.ReadLine(); 

     } 

     static void OnEvt1(IAsyncResult result) 
     { 
      Console.WriteLine("Setting1"); 
      var handle = result.AsyncWaitHandle; 
      ((ManualResetEvent)handle).Set(); 
     } 

     static void OnEvt2(IAsyncResult result) 
     { 
      Thread.Sleep(2000); 
      Console.WriteLine("Setting2"); 
      var handle = result.AsyncWaitHandle; 
      ((ManualResetEvent)handle).Set(); 
     } 

    } 

    public class ManualResetResult : IAsyncResult 
    { 
     private object _state; 
     private ManualResetEvent _handle; 

     public ManualResetResult(object state, ManualResetEvent handle) 
     { 
      _state = state; 
      _handle = handle; 
     } 

     #region IAsyncResult Members 

     public object AsyncState 
     { 
      get { return _state; } 
     } 

     public WaitHandle AsyncWaitHandle 
     { 
      get { return _handle; } 
     } 

     public bool CompletedSynchronously 
     { 
      get { throw new NotImplementedException(); } 
     } 

     public bool IsCompleted 
     { 
      get { throw new NotImplementedException(); } 
     } 

     #endregion 
    } 
}