2015-09-18 8 views
11

Quale sarebbe l'equivalente asincrono (attendibile) di AutoResetEvent?Awaitable AutoResetEvent

Se nella sincronizzazione dei thread classica vorremmo usare qualcosa di simile:

AutoResetEvent signal = new AutoResetEvent(false); 

    void Thread1Proc() 
    { 
     //do some stuff 
     //.. 
     //.. 

     signal.WaitOne(); //wait for an outer thread to signal we are good to continue 

     //do some more stuff 
     //.. 
     //.. 
    } 

    void Thread2Proc() 
    { 
     //do some stuff 
     //.. 
     //.. 

     signal.Set(); //signal the other thread it's good to go 

     //do some more stuff 
     //.. 
     //.. 
    } 

Speravo che nel nuovo modo asincrono di fare le cose, qualcosa di simile sarebbe venuto per essere:

SomeAsyncAutoResetEvent asyncSignal = new SomeAsyncAutoResetEvent(); 

async void Task1Proc() 
{ 
    //do some stuff 
    //.. 
    //.. 

    await asyncSignal.WaitOne(); //wait for an outer thread to signal we are good to continue 

    //do some more stuff 
    //.. 
    //.. 
} 

async void Task2Proc() 
{ 
    //do some stuff 
    //.. 
    //.. 

    asyncSignal.Set(); //signal the other thread it's good to go 

    //do some more stuff 
    //.. 
    //.. 
} 

Ho visto altre soluzioni su misura, ma quello che sono riuscito a mettere le mani su, ad un certo punto nel tempo, implica ancora il blocco di un thread. Non voglio questo solo per il gusto di usare la nuova sintassi di attesa. Sto cercando un meccanismo di segnalazione vero e attendibile che non blocchi alcun thread.

È qualcosa che mi manca nella libreria parallela attività?

MODIFICA: solo per chiarire: SomeAsyncAutoResetEvent è un nome di classe completamente costituito utilizzato come segnaposto nel mio esempio.

+0

Per one-time-uso, un 'TaskCompletionSource' il cui risultato viene ignorato dal compito in attesa. –

+0

https://gist.github.com/AArnott/1084951 forse? –

+0

@MatthewWatson Vedo che utilizza un blocco, che bloccherà un thread dal pool di thread. Speravo in qualcosa che non riguardasse un thread bloccato. –

risposta

9

Se si desidera creare il proprio, Stephen Toub has the definitive blog post on the subject.

Se si desidera utilizzare uno che è già stato scritto, I have one in my AsyncEx library. AFAIK, non ci sono altre opzioni al momento della stesura di questo articolo.

+1

Perché non dovrebbe funzionare un 'nuovo SemaphoreSlim (1),' WaitOne() 'è' WaitAsync() 'e' Set() 'diventa' Release() ' –

+1

ARE e Semafori sono molto simili (anche se di solito vengono usati in modo diverso). La differenza semantica arriva se la primitiva viene segnalata quando è già impostata. –

6

Ecco la fonte di Stephen Toub AsyncAutoResetEvent, nel caso in cui il suo blog non sia in linea.

public class AsyncAutoResetEvent 
{ 
    private static readonly Task s_completed = Task.FromResult(true); 
    private readonly Queue<TaskCompletionSource<bool>> m_waits = new Queue<TaskCompletionSource<bool>>(); 
    private bool m_signaled; 

    public Task WaitAsync() 
    { 
     lock (m_waits) 
     { 
      if (m_signaled) 
      { 
       m_signaled = false; 
       return s_completed; 
      } 
      else 
      { 
       var tcs = new TaskCompletionSource<bool>(); 
       m_waits.Enqueue(tcs); 
       return tcs.Task; 
      } 
     } 
    } 

    public void Set() 
    { 
     TaskCompletionSource<bool> toRelease = null; 

     lock (m_waits) 
     { 
      if (m_waits.Count > 0) 
       toRelease = m_waits.Dequeue(); 
      else if (!m_signaled) 
       m_signaled = true; 
     } 

     toRelease?.SetResult(true); 
    } 
} 
1

Ecco una versione che ho preparato che consente di specificare un timeout. Deriva dalla soluzione di Stephen Toub. È passato attraverso i test di base dello scenario.

public class AsyncAutoResetEvent 
{ 
    readonly LinkedList<TaskCompletionSource<bool>> waiters = 
     new LinkedList<TaskCompletionSource<bool>>(); 

    bool isSignaled; 

    public AsyncAutoResetEvent(bool signaled) 
    { 
     this.isSignaled = signaled; 
    } 

    public Task<bool> WaitAsync(TimeSpan timeout) 
    { 
     return this.WaitAsync(timeout, CancellationToken.None); 
    } 

    public async Task<bool> WaitAsync(TimeSpan timeout, CancellationToken cancellationToken) 
    { 
     TaskCompletionSource<bool> tcs; 

     lock (this.waiters) 
     { 
      if (this.isSignaled) 
      { 
       this.isSignaled = false; 
       return true; 
      } 
      else if (timeout == TimeSpan.Zero) 
      { 
       return this.isSignaled; 
      } 
      else 
      { 
       tcs = new TaskCompletionSource<bool>(); 
       this.waiters.AddLast(tcs); 
      } 
     } 

     Task winner = await Task.WhenAny(tcs.Task, Task.Delay(timeout, cancellationToken)); 
     if (winner == tcs.Task) 
     { 
      // The task was signaled. 
      return true; 
     } 
     else 
     { 
      // We timed-out; remove our reference to the task. 
      // This is an O(n) operation since waiters is a LinkedList<T>. 
      lock (this.waiters) 
      { 
       bool removed = this.waiters.Remove(tcs); 
       Debug.Assert(removed); 
       return false; 
      } 
     } 
    } 

    public void Set() 
    { 
     TaskCompletionSource<bool> toRelease = null; 

     lock (this.waiters) 
     { 
      if (this.waiters.Count > 0) 
      { 
       // Signal the first task in the waiters list. 
       toRelease = this.waiters.First.Value; 
       this.waiters.RemoveFirst(); 
      } 
      else if (!this.isSignaled) 
      { 
       // No tasks are pending 
       this.isSignaled = true; 
      } 
     } 

     if (toRelease != null) 
     { 
      toRelease.SetResult(true); 
     } 
    } 
} 
+1

Penso che questo.waiters dovrebbe essere bloccato nel percorso di manipolazione Remove (tcs)? – HelloSam

+0

@ CiaoSam Penso che tu abbia ragione! Fisso. Grazie per averlo indicato. –

1

penso che ci sia buon esempio su MSDN: https://msdn.microsoft.com/en-us/library/hh873178%28v=vs.110%29.aspx#WHToTap

public static Task WaitOneAsync(this WaitHandle waitHandle) 
{ 
    if (waitHandle == null) 
     throw new ArgumentNullException("waitHandle"); 

    var tcs = new TaskCompletionSource<bool>(); 
    var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, 
     delegate { tcs.TrySetResult(true); }, null, -1, true); 
    var t = tcs.Task; 
    t.ContinueWith((antecedent) => rwh.Unregister(null)); 
    return t; 
} 
Problemi correlati