2012-10-05 34 views
36

Después de preguntar this question, me pregunto si es posible esperar a que se dispare un evento, y luego obtener los datos del evento y devolver parte del mismo. Más o menos así:Cómo bloquear hasta que se active un evento en C#

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

Asegúrese de que la solución que proporcione devuelva el valor directamente en lugar de obtenerlo de otra cosa. Pregunto si el método anterior está disponible de alguna manera. Sé de Auto/ManuelResetEvent, pero no sé si devuelven el valor directamente como lo hice anteriormente.

Actualización: He declarado un evento usando MyEventHandler (que contiene un campo Message). Tengo un método en otro hilo llamado ReadLine esperando a que el evento se dispare. Cuando el evento activa el método WaitForValue (parte de la escena de manejo de eventos) devuelve los eventos args, que contienen el mensaje. A continuación, ReadLine devuelve el mensaje a lo que lo llamó.

The accepted answer a that question Lo pregunté fue lo que hice, pero simplemente no me siento del todo bien. Casi parece que algo podría pasarle a los datos entre la activación ManuelResetEvent y el programa que recupera los datos y los devuelve.

Actualización: El principal problema con el Auto/ManualResetEvent es que es demasiado vulnerable. Un hilo podría esperar el evento y luego no dar tiempo suficiente para que nadie lo obtenga antes de cambiarlo a otra cosa. ¿Hay alguna manera de usar bloqueos o algo más? Tal vez usando declaraciones get y set.

+0

¿qué pasa con un bucle while: 'while (someGlobalvar);' 'cahnge someGlobalvar' en una función que ASIGN al evento. – elyashiv

+9

está en espera activa, y es una muy mala solución –

+0

Ahora, 2 años después, parece que debería simplemente hacer WaitTwo que devolvería un objeto, y '* ResetEvent.Set (datos de objeto)'. Todavía tendría que preocuparme por las referencias a los objetos, pero eso es normal. Al menos el puntero no cambiaría. –

Respuesta

25

Puede usar ManualResetEvent. Restablezca el evento antes de disparar el hilo secundario y luego use el método WaitOne() para bloquear el hilo actual. A continuación, puede configurar el hilo secundario para configurar el evento ManualResetEvent, lo que provocaría que el hilo principal continuara. Algo como esto:

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 
} 
+0

Esto no devuelve el valor directamente. Ya lo intenté en la otra pregunta. ¿No hay algo como lo que estoy preguntando? –

2

Un muy fácil tipo de evento que puede esperar es la ManualResetEvent, y aún mejor, la ManualResetEventSlim.

Tienen un método WaitOne() que hace exactamente eso. Puedes esperar por siempre, o establecer un tiempo de espera, o un "token de cancelación", que es una forma de que decidas dejar de esperar el evento (si deseas cancelar tu trabajo o se te pide que salgas).

Los despide llamando al Set().

Here es el documento.

+1

Pero eso no devuelve ningún tipo de valor. –

+0

¿Puede editar el qu y agregar una visión general de los diferentes componentes y sus interacciones? Nos ayudará a ver mejor lo que está tratando de lograr y, con un poco de suerte, a mejorar el diseño. – Michael

-1

Si estás dispuesto a utilizar las extensiones reactivas de Microsoft, entonces esto puede funcionar muy bien:

public class Foo 
{ 
    public delegate void MyEventHandler(object source, MessageEventArgs args); 
    public event MyEventHandler _event; 
    public string ReadLine() 
    { 
     return Observable 
      .FromEventPattern<MyEventHandler, MessageEventArgs>(
       h => this._event += h, 
       h => this._event -= h) 
      .Select(ep => ep.EventArgs.Message) 
      .First(); 
    } 
    public void SendLine(string message) 
    { 
     _event(this, new MessageEventArgs() { Message = message }); 
    } 
} 

public class MessageEventArgs : EventArgs 
{ 
    public string Message; 
} 

que puede utilizar de esta manera:

var foo = new Foo(); 

ThreadPoolScheduler.Instance 
    .Schedule(
     TimeSpan.FromSeconds(5.0), 
     () => foo.SendLine("Bar!")); 

var resp = foo.ReadLine(); 

Console.WriteLine(resp); 

que tenía que llamar al mensaje SendLine en un hilo diferente para evitar el bloqueo, pero este código muestra que funciona como se esperaba.

13

Si el método actual es asíncrono, puede usar TaskCompletionSource. Cree un campo al que puedan acceder el controlador de eventos y el método actual.

TaskCompletionSource<bool> tsc = null; 

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

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

Este ejemplo utiliza un formulario que tiene un bloque de texto llamado WelcomeTitle y dos botones. Cuando se hace clic en el primer botón, se inicia el evento de clic pero se detiene en la línea de espera. Cuando se hace clic en el segundo botón, la tarea se completa y el texto de WelcomeTitle se actualiza. Si desea el tiempo de espera, así luego cambiar

await tsc.Task; 

a

await Task.WhenAny(tsc.Task, Task.Delay(25000)); 
if (tsc.Task.IsCompleted) 
    WelcomeTitle.Text = "Task Completed"; 
else 
    WelcomeTitle.Text = "Task Timed Out"; 
+0

No necesita asincronizar para usar TaskCompletionSource –

+0

Eso es cierto, pero lo necesita si desea esperar la tarea sin bloquear la interfaz de usuario. – Adam

Cuestiones relacionadas