Comment puis-je attendre qu’un événement C # soit déclenché?

J’ai une classe d’ Sender qui envoie un Message sur un IChannel :

 public class MessageEventArgs : EventArgs { public Message Message { get; private set; } public MessageEventArgs(Message m) { Message = m; } } public interface IChannel { public event EventHandler MessageReceived; void Send(Message m); } public class Sender { public const int MaxWaitInMs = 5000; private IChannel _c = ...; public Message Send(Message m) { _c.Send(m); // wait for MaxWaitInMs to get an event from _c.MessageReceived // return the message or null if no message was received in response } } 

Lorsque nous envoyons des messages, IChannel donne parfois une réponse en fonction du type de Message envoyé en soulevant l’événement MessageReceived . Les arguments d’événement contiennent le message d’intérêt.

Je veux Sender.Send() méthode Sender.Send() attende un court instant pour voir si cet événement est déclenché. Si tel est le cas, je renverrai sa propriété MessageEventArgs.Message . Sinon, je retourne un Message nul.

Comment puis-je attendre de cette façon? Je préfèrerais ne pas avoir à faire les manœuvres avec ManualResetEvents , etc., donc restr ManualResetEvents à des event réguliers serait optimal pour moi.

Utilisez un AutoResetEvent .

Donnez-moi quelques minutes et je vais lancer un échantillon.

C’est ici:

 public class Sender { public static readonly TimeSpan MaxWait = TimeSpan.FromMilliseconds(5000); private IChannel _c; private AutoResetEvent _messageReceived; public Sender() { // initialize _c this._messageReceived = new AutoResetEvent(false); this._c.MessageReceived += this.MessageReceived; } public Message Send(Message m) { this._c.Send(m); // wait for MaxWaitInMs to get an event from _c.MessageReceived // return the message or null if no message was received in response // This will wait for up to 5000 ms, then throw an exception. this._messageReceived.WaitOne(MaxWait); return null; } public void MessageReceived(object sender, MessageEventArgs e) { //Do whatever you need to do with the message this._messageReceived.Set(); } } 

Avez-vous essayé d’affecter la fonction à un appel de manière asynchrone à un délégué, puis d’appeler mydelegateinstance.BeginInvoke?

Linky pour référence.

Avec l’exemple ci-dessous, il suffit d’appeler

 FillDataSet(ref table, ref dataset); 

et ça va marcher comme par magie. 🙂

 #region DataSet manipulation ///Fills a the distance table of a dataset private void FillDataSet(ref DistanceDataTableAdapter taD, ref MyDataSet ds) { using (var myMRE = new ManualResetEventSlim(false)) { ds.EnforceConstraints = false; ds.Distance.BeginLoadData(); Func distanceFill = taD.Fill; distanceFill.BeginInvoke(ds.Distance, FillCallback, new object[] { distanceFill, myMRE }); WaitHandle.WaitAll(new []{ myMRE.WaitHandle }); ds.Distance.EndLoadData(); ds.EnforceConstraints = true; } } ///  /// Callback used when filling a table asynchronously. ///  /// Represents the status of the asynchronous operation. private void FillCallback(IAsyncResult result) where MyDataTable: DataTable { var state = result.AsyncState as object[]; Debug.Assert((state != null) && (state.Length == 2), "State variable is either null or an invalid number of parameters were passed."); var fillFunc = state[0] as Func; var mre = state[1] as ManualResetEventSlim; Debug.Assert((mre != null) && (fillFunc != null)); int rowsAffected = fillFunc.EndInvoke(result); Debug.WriteLine(" Rows: " + rowsAffected.ToSsortingng()); mre.Set(); } 

Peut-être que votre méthode MessageReceived devrait simplement signaler une valeur à une propriété de votre interface IChannel, tout en implémentant le gestionnaire d’événements INotifyPropertyChanged, afin que vous soyez averti lorsque la propriété est modifiée.

Ce faisant, votre classe d’expéditeur pourrait être mise en boucle jusqu’à ce que le temps d’attente maximal soit écoulé ou à chaque fois que le gestionnaire d’événements PropertyChanged se produira, interrompant la boucle avec succès. Si votre boucle n’est pas cassée, le message sera considéré comme jamais reçu.

Exemple utile avec AutoResetEvent :

  using System; using System.Threading; class WaitOne { static AutoResetEvent autoEvent = new AutoResetEvent(false); static void Main() { Console.WriteLine("Main starting."); ThreadPool.QueueUserWorkItem( new WaitCallback(WorkMethod), autoEvent); // Wait for work method to signal. autoEvent.WaitOne(); Console.WriteLine("Work method signaled.\nMain ending."); } static void WorkMethod(object stateInfo) { Console.WriteLine("Work starting."); // Simulate time spent working. Thread.Sleep(new Random().Next(100, 2000)); // Signal that work is finished. Console.WriteLine("Work ending."); ((AutoResetEvent)stateInfo).Set(); } } 

WaitOne est vraiment le bon outil pour ce travail. En bref, vous voulez attendre entre 0 et MaxWaitInMs millisecondes pour qu’un travail soit terminé. Vous avez vraiment le choix, interroger pour l’achèvement ou synchroniser les threads avec une construction pouvant attendre un laps de temps arbitraire.

Puisque vous savez bien comment faire cela, pour la postérité, je posterai la version du sondage:

 MessageEventArgs msgArgs = null; var callback = (object o, MessageEventArgs args) => { msgArgs = args; }; _c.MessageReceived += callback; _c.Send(m); int msLeft = MaxWaitInMs; while (msgArgs == null || msLeft >= 0) { Thread.Sleep(100); msLeft -= 100; // you should measure this instead with say, Stopwatch } _c.MessageRecieved -= callback;