2012-10-05 26 views
36

Dopo aver chiesto this question, mi chiedo se è possibile attendere che venga generato un evento, quindi ottenere i dati dell'evento e restituirne una parte. Un modo simile al seguente:Come bloccare fino a quando un evento viene attivato C#

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

Assicurati che qualsiasi soluzione fornita restituisca direttamente il valore piuttosto che ottenerlo da qualcos'altro. Sto chiedendo se il metodo sopra è disponibile in qualche modo. Conosco Auto/ManuelResetEvent, ma non so che restituiscono il valore direttamente come ho fatto sopra.

Aggiornamento: Ho dichiarato un evento utilizzando MyEventHandler (che contiene un campo Message). Ho un metodo in un altro thread chiamato ReadLine in attesa dell'inizio dell'evento. Quando l'evento attiva il metodo WaitForValue (parte della scena di gestione degli eventi) restituisce l'evento args, che contiene il messaggio. Il messaggio viene quindi restituito da ReadLine a qualsiasi cosa lo abbia chiamato.

The accepted answer a that question Ho chiesto cosa fosse, ma non mi sembra giusto. Sembra quasi che qualcosa potrebbe accadere ai dati tra l'attivazione di ManuelResetEvent e il programma che recupera i dati e li restituisce.

Aggiornamento: Il problema principale con Auto/ManualResetEvent è che è troppo vulnerabile. Un thread potrebbe attendere l'evento e quindi non dare abbastanza tempo a chiunque altro per ottenerlo prima di cambiarlo in qualcos'altro. C'è un modo per usare le serrature o qualcos'altro? Forse usando le istruzioni get e set.

+0

che dire di un ciclo while: 'while (someGlobalvar);' 'cahnge someGlobalvar' in una funzione si Asign all'evento. – elyashiv

+9

è in attesa, ed è una pessima soluzione –

+0

Ora, 2 anni dopo, sembra che dovrei solo rendere WaitTwo che restituirebbe un oggetto, e '* ResetEvent.Set (dati dell'oggetto)'. Avrei ancora bisogno di preoccuparmi dei riferimenti agli oggetti, ma è normale. Almeno il puntatore non cambierebbe. –

risposta

25

È possibile utilizzare ManualResetEvent. Reimpostare l'evento prima di attivare il thread secondario e quindi utilizzare il metodo WaitOne() per bloccare il thread corrente. È quindi possibile impostare thread secondario su ManualResetEvent che causi la continuazione del thread principale. Qualcosa di simile a questo:

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

Questo non restituisce direttamente il valore. L'ho già provato nell'altra domanda. Non c'è niente di cui ti sto chiedendo? –

2

Una molto facile tipo di evento si può aspettare che è il ManualResetEvent, e anche meglio, il ManualResetEventSlim.

Hanno un metodo WaitOne() che fa esattamente questo. Puoi aspettare per sempre, o impostare un timeout, o un "token di cancellazione" che è un modo per te di decidere di smettere di aspettare l'evento (se vuoi cancellare il tuo lavoro, o se la tua app è obbligata ad uscire).

Si spara chiamando Set().

Here è il doc.

+1

Ma questo non restituisce alcun tipo di valore. –

+0

È possibile modificare la qu e aggiungere una panoramica dei diversi componenti e delle loro interazioni? Ci aiuterà a vedere meglio ciò che stai cercando di raggiungere e, auspicabilmente, a migliorare il design. – Michael

-1

Se sei felice di utilizzare il Microsoft reattivi estensioni, allora questo può funzionare bene:

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; 
} 

posso usare in questo modo:

var foo = new Foo(); 

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

var resp = foo.ReadLine(); 

Console.WriteLine(resp); 

avevo bisogno di chiamare il messaggio SendLine su un thread diverso per evitare il blocco, ma questo codice mostra che funziona come previsto.

13

Se il metodo corrente è asincrono, è possibile utilizzare TaskCompletionSource. Creare un campo a cui possono accedere il gestore eventi e il metodo corrente.

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); 
    } 

Questo esempio utilizza un modulo con un blocco di testo denominato WelcomeTitle e due pulsanti. Quando si fa clic sul primo pulsante, si avvia l'evento click, ma si ferma sulla linea di attesa. Quando si fa clic sul secondo pulsante, l'attività viene completata e il testo del WelcomeTitle viene aggiornato. Se si vuole timeout e quindi modificare

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

Non è necessario async per utilizzare TaskCompletionSource –

+0

Questo è vero, ma è necessario se si desidera attendere l'attività senza bloccare l'interfaccia utente. – Adam

Problemi correlati