web-dev-qa-db-fra.com

Comment bloquer jusqu'à ce qu'un événement soit déclenché en c #

Après avoir demandé cette question , je me demande s’il est possible d’attendre qu’un événement soit déclenché, puis d’obtenir les données d’événement et d’en retourner une partie. Un peu comme ça:

private event MyEventHandler event;
public string ReadLine(){ return event.waitForValue().Message; }
...
event("My String");
...elsewhere...
var resp = ReadLine();

Assurez-vous que la solution que vous proposez renvoie directement la valeur plutôt que de l'obtenir autrement. Je demande si la méthode ci-dessus est disponible d'une certaine manière. Je connais Auto/ManuelResetEvent, mais je ne sais pas s'ils renvoient la valeur directement comme je l'ai fait ci-dessus.

Mise à jour: J'ai déclaré un événement à l'aide de MyEventHandler (qui contient un champ Message). J'ai une méthode dans un autre thread appelé ReadLine en attente du déclenchement de l'événement. Lorsque l'événement se déclenche, la méthode WaitForValue (qui fait partie de la scène de gestion des événements) renvoie les arguments de l'événement, qui contiennent le message. Le message est ensuite renvoyé par ReadLine à celui qui l’avait appelé.

La réponse acceptée à cette question J'ai demandé ce que j'ai fait, mais cela ne me semble pas tout à fait correct. Il semble presque que quelque chose puisse arriver aux données entre le déclenchement de ManuelResetEvent et le programme récupérant les données et les renvoyant.

Mise à jour: Le problème principal avec le Auto/ManualResetEvent c'est qu'il est trop vulnérable. Un fil pourrait attendre l'événement et ne pas laisser suffisamment de temps à quelqu'un d'autre pour l'obtenir avant de le remplacer par autre chose. Y a-t-il un moyen d'utiliser des serrures ou autre chose? Peut-être en utilisant des instructions get et set.

53
Arlen Beiler

Vous pouvez utiliser ManualResetEvent . Réinitialisez l'événement avant de déclencher le thread secondaire, puis utilisez la méthode WaitOne () pour bloquer le thread actuel. Vous pouvez ensuite faire en sorte que le thread secondaire définisse ManualResetEvent, de sorte que le thread principal continue. Quelque chose comme ça:

ManualResetEvent oSignalEvent = new ManualResetEvent(false);

void SecondThread(){
    //DoStuff
    oSignalEvent.Set();
}

void Main(){
    //DoStuff
    //Call second thread
    System.Threading.Thread oSecondThread = new System.Threading.Thread(SecondThread);
    oSecondThread.Start();

    oSignalEvent.WaitOne(); //This thread will block here until the reset event is sent.
    oSignalEvent.Reset();
    //Do more stuff
}
38
Vinoth

Si la méthode actuelle est asynchrone, vous pouvez utiliser TaskCompletionSource. Créez un champ auquel le gestionnaire d'événements et la méthode en cours peuvent accéder.

    TaskCompletionSource<bool> tcs = null;

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        tcs = new TaskCompletionSource<bool>();
        await tcs.Task;
        WelcomeTitle.Text = "Finished work";
    }

    private void Button_Click2(object sender, RoutedEventArgs e)
    {
        tcs?.TrySetResult(true);
    }

Cet exemple utilise un formulaire comportant un bloc de texte appelé WelcomeTitle et deux boutons. Lorsque le premier bouton est cliqué, l'événement click se déclenche mais s'arrête à la ligne d'attente. Lorsque le deuxième bouton est cliqué, la tâche est terminée et le texte WelcomeTitle est mis à jour. Si vous voulez aussi expirer, alors changez

await tcs.Task;

à

await Task.WhenAny(tcs.Task, Task.Delay(25000));
if (tcs.Task.IsCompleted)
    WelcomeTitle.Text = "Task Completed";
else
    WelcomeTitle.Text = "Task Timed Out";
32
Adam

Un type d'événement très simple que vous pouvez attendre est le ManualResetEvent, et encore mieux le ManualResetEventSlim.

Ils ont une méthode WaitOne() qui fait exactement cela. Vous pouvez attendre indéfiniment, ou définir un délai d'expiration ou un "jeton d'annulation", qui vous permet de décider de ne plus attendre l'événement (si vous souhaitez annuler votre travail ou demander à votre application de quitter).

Vous les tirez en appelant Set().

Ici est le doc.

3
mbarthelemy