run programming parallel net examples example async asp c# .net multithreading asynchronous task-parallel-library

c# - programming - Enfriar AutoResetEvent



task.run c# (4)

¿Cuál sería el equivalente asíncrono (esperable) de AutoResetEvent?

Si en la sincronización clásica de hilos usaríamos algo como esto:

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 //.. //.. }

Esperaba que en la nueva forma asíncrona de hacer las cosas, algo así sucediera:

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 //.. //.. }

He visto otras soluciones hechas a la medida, pero en algún momento, lo que logré tener en mis manos, aún involucra bloquear un hilo. No quiero esto solo por el uso de la nueva sintaxis de espera. Estoy buscando un verdadero mecanismo de señalización que no pueda bloquear ningún hilo.

¿Es algo que me falta en la biblioteca paralela de tareas?

EDITAR: Solo para aclarar: SomeAsyncAutoResetEvent es un nombre de clase totalmente inventado que se utiliza como marcador de posición en mi ejemplo.


Aquí está la fuente de AsyncAutoResetEvent Stephen Toub, en caso de que su blog se desconecte.

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


Aquí hay una versión que cociné que le permite especificar un tiempo de espera. Se deriva de la solución de Stephen Toub. Actualmente utilizamos esto en cargas de trabajo de producción.

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() { lock (this.waiters) { if (this.waiters.Count > 0) { // Signal the first task in the waiters list. This must be done on a new // thread to avoid stack-dives and situations where we try to complete the // same result multiple times. TaskCompletionSource<bool> tcs = this.waiters.First.Value; Task.Run(() => tcs.SetResult(true)); this.waiters.RemoveFirst(); } else if (!this.isSignaled) { // No tasks are pending this.isSignaled = true; } } } public override string ToString() { return $"Signaled: {this.isSignaled.ToString()}, Waiters: {this.waiters.Count.ToString()}"; } }


Creo que hay un buen ejemplo en 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; }