run method explained español ejemplos ejemplo create await async c# .net asynchronous task-parallel-library async-await

method - task c# ejemplo



Use Task.Run() en el método síncrono para evitar el interbloqueo en espera en el método async? (5)

Con un pequeño contexto de sincronización personalizada, la función de sincronización puede esperar la finalización de la función asíncrona, sin crear un interbloqueo. El hilo original se conserva, por lo que el método de sincronización utiliza el mismo hilo antes y después de llamar a la función asíncrona. Aquí hay un pequeño ejemplo para la aplicación WinForms.

Imports System.Threading Imports System.Runtime.CompilerServices Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load SyncMethod() End Sub '' waiting inside Sync method for finishing async method Public Sub SyncMethod() Dim sc As New SC sc.WaitForTask(AsyncMethod()) sc.Release() End Sub Public Async Function AsyncMethod() As Task(Of Boolean) Await Task.Delay(1000) Return True End Function End Class Public Class SC Inherits SynchronizationContext Dim OldContext As SynchronizationContext Dim ContextThread As Thread Sub New() OldContext = SynchronizationContext.Current ContextThread = Thread.CurrentThread SynchronizationContext.SetSynchronizationContext(Me) End Sub Dim DataAcquired As New Object Dim WorkWaitingCount As Long = 0 Dim ExtProc As SendOrPostCallback Dim ExtProcArg As Object <MethodImpl(MethodImplOptions.Synchronized)> Public Overrides Sub Post(d As SendOrPostCallback, state As Object) Interlocked.Increment(WorkWaitingCount) Monitor.Enter(DataAcquired) ExtProc = d ExtProcArg = state AwakeThread() Monitor.Wait(DataAcquired) Monitor.Exit(DataAcquired) End Sub Dim ThreadSleep As Long = 0 Private Sub AwakeThread() If Interlocked.Read(ThreadSleep) > 0 Then ContextThread.Resume() End Sub Public Sub WaitForTask(Tsk As Task) Dim aw = Tsk.GetAwaiter If aw.IsCompleted Then Exit Sub While Interlocked.Read(WorkWaitingCount) > 0 Or aw.IsCompleted = False If Interlocked.Read(WorkWaitingCount) = 0 Then Interlocked.Increment(ThreadSleep) ContextThread.Suspend() Interlocked.Decrement(ThreadSleep) Else Interlocked.Decrement(WorkWaitingCount) Monitor.Enter(DataAcquired) Dim Proc = ExtProc Dim ProcArg = ExtProcArg Monitor.Pulse(DataAcquired) Monitor.Exit(DataAcquired) Proc(ProcArg) End If End While End Sub Public Sub Release() SynchronizationContext.SetSynchronizationContext(OldContext) End Sub End Class

ACTUALIZAR El propósito de esta pregunta es obtener una respuesta simple sobre Task.Run() y deadlocking. Entiendo mucho el razonamiento teórico para no mezclar asincronía y sincronización, y los tomo en serio. No estoy por encima de aprender cosas nuevas de otros; Trato de hacer eso siempre que puedo. Hay momentos en que todo lo que un chico necesita es una respuesta técnica ...

Tengo un método Dispose() que necesita llamar a un método asíncrono. Como el 95% de mi código es asíncrono, la refactorización no es la mejor opción. Tener un IAsyncDisposable (entre otras características) que sea compatible con el framework sería ideal, pero aún no lo hemos logrado. Entonces, mientras tanto, necesito encontrar una forma confiable de llamar a los métodos asíncronos desde un método sincrónico sin interbloqueo.

Preferiría no usar ConfigureAwait(false) porque eso deja la responsabilidad dispersa en todo mi código para que el destinatario se comporte de cierta manera, en caso de que la persona que llama sea sincrónica. Prefiero hacer algo en el método sincrónico ya que es el insector desviado.

Después de leer el comentario de Stephen Cleary en otra pregunta que Task.Run() siempre programa en el grupo de subprocesos incluso en métodos asíncronos, me hizo pensar.

En .NET 4.5 en ASP.NET o cualquier otro contexto de sincronización que programe tareas para el hilo actual / el mismo hilo, si tengo un método asincrónico:

private async Task MyAsyncMethod() { ... }

Y quiero llamarlo desde un método sincrónico, ¿puedo usar Task.Run() con Wait() para evitar interbloqueos, ya que pone en cola el método async del grupo de subprocesos?

private void MySynchronousMethodLikeDisposeForExample() { // MyAsyncMethod will get queued to the thread pool // so it shouldn''t deadlock with the Wait() ?? Task.Run((Func<Task>)MyAsyncMethod).Wait(); }


Esta es mi forma de evitar el interbloqueo cuando tengo que llamar al método asincrónico sincrónicamente y el subproceso puede ser subproceso UI:

public static T GetResultSafe<T>(this Task<T> task) { if (SynchronizationContext.Current == null) return task.Result; if (task.IsCompleted) return task.Result; var tcs = new TaskCompletionSource<T>(); task.ContinueWith(t => { var ex = t.Exception; if (ex != null) tcs.SetException(ex); else tcs.SetResult(t.Result); }, TaskScheduler.Default); return tcs.Task.Result; }


Este código no se estancará exactamente por los motivos que destacó en la pregunta: el código siempre se ejecuta sin contexto de sincronización (ya que usa el grupo de subprocesos) y Wait simplemente bloqueará el método de subprocesamiento hasta que el método retorne.


Parece que entiendes los riesgos que conlleva tu pregunta, así que me saltearé la conferencia.

Para responder a su pregunta real: Sí, puede usar Task.Run para descargar ese trabajo a un hilo de ThreadPool que no tiene un SynchronizationContext por lo que no existe un riesgo real de un punto muerto.

Sin embargo , usar otro subproceso simplemente porque no tiene SC es algo así como un truco y podría ser costoso, ya que la programación del trabajo que se debe realizar en el ThreadPool tiene sus costos.

Una solución mejor y más clara IMO sería simplemente eliminar el SC por el momento usando SynchronizationContext.SetSynchronizationContext y restaurarlo después. Esto se puede encapsular fácilmente en un IDisposable para que pueda usarlo en un ámbito de using :

public static class NoSynchronizationContextScope { public static Disposable Enter() { var context = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(null); return new Disposable(context); } public struct Disposable : IDisposable { private readonly SynchronizationContext _synchronizationContext; public Disposable(SynchronizationContext synchronizationContext) { _synchronizationContext = synchronizationContext; } public void Dispose() => SynchronizationContext.SetSynchronizationContext(_synchronizationContext); } }

Uso:

private void MySynchronousMethodLikeDisposeForExample() { using (NoSynchronizationContextScope.Enter()) { MyAsyncMethod().Wait(); } }


Si absolutamente debe llamar al método asíncrono desde uno sincrónico, asegúrese de usar ConfigureAwait(false) dentro de sus llamadas al método asíncrono para evitar la captura del contexto de sincronización.

Esto debería mantenerse, pero es inestable en el mejor de los casos. Aconsejaría pensar en refactorizar. en lugar.