c# - understanding - ¿Cómo ejecutaría un método asíncrono de tarea<T> de forma sincrónica?
task.run c# (24)
Estoy aprendiendo sobre async / await, y me encontré con una situación en la que necesito llamar un método asíncrono de forma síncrona. ¿Cómo puedo hacer eso?
La mejor respuesta es que no , con los detalles que dependen de lo que es la "situación".
¿Es una propiedad getter / setter? En la mayoría de los casos, es mejor tener métodos asíncronos que "propiedades asíncronas". (Para más información, vea mi publicación de blog sobre propiedades asíncronas ).
¿Es esta una aplicación MVVM y desea realizar un enlace de datos asíncrono? Luego use algo como mi NotifyTask
, como se describe en mi artículo de MSDN sobre enlace de datos asíncrono .
¿Es un constructor? Entonces probablemente querrás considerar un método de fábrica asíncrono. (Para más información, vea mi blog en constructores asíncronos ).
Casi siempre hay una mejor respuesta que hacer sync-over-async.
Si no es posible para su situación (y lo sabe haciendo una pregunta aquí que describe la situación ), le recomiendo que solo utilice un código síncrono. Async todo el camino es mejor; Sincronizar todo el camino es el segundo mejor. No se recomienda la sincronización sobre asíncrono.
Sin embargo, hay un puñado de situaciones donde es necesario sincronizar sobre asíncrono. Específicamente, el código de llamada lo restringe para que tenga que estar sincronizado (y no tiene absolutamente ninguna manera de repensar o reestructurar su código para permitir la asincronía), y debe llamar al código asíncrono. Esta es una situación muy rara, pero surge de vez en cuando.
En ese caso, necesitaría usar uno de los trucos descritos en mi artículo sobre el desarrollo async
Brownfield , específicamente:
- Bloqueo (por ejemplo,
GetAwaiter().GetResult()
). Tenga en cuenta que esto puede causar puntos muertos (como describo en mi blog). - Ejecutar el código en un subproceso de grupo de subprocesos (por ejemplo,
Task.Run(..).GetAwaiter().GetResult()
). Tenga en cuenta que esto solo funcionará si el código asíncrono se puede ejecutar en un subproceso de grupo de subprocesos (es decir, no depende de un contexto de UI o ASP.NET). - Bucles de mensajes anidados. Tenga en cuenta que esto solo funcionará si el código asíncrono solo asume un contexto de un solo hilo, no un tipo de contexto específico (muchos UI y código ASP.NET esperan un contexto específico).
Los bucles de mensajes anidados son los más peligrosos de todos los hacks, ya que causan una re-entrancy . La re-entrada es extremadamente difícil de razonar, y (IMO) es la causa de la mayoría de los errores de aplicaciones en Windows. En particular, si está en el subproceso de la interfaz de usuario y bloquea en una cola de trabajo (a la espera de que se complete el trabajo asíncrono), entonces el CLR realiza un bombeo de mensajes por usted, en realidad manejará algunos mensajes de Win32 desde su codigo Ah, y no tienes idea de qué mensajes, cuando Chris Brumme dice "¿No sería genial saber exactamente qué se bombeará? Desafortunadamente, bombear es un arte negro que está más allá de la comprensión mortal". , entonces realmente no tenemos ninguna esperanza de saber.
Entonces, cuando bloqueas así en un subproceso de la interfaz de usuario, estás pidiendo problemas. Otra cita del mismo artículo: "De vez en cuando, los clientes dentro o fuera de la empresa descubren que estamos bombeando mensajes durante el bloqueo administrado en un STA [subproceso de la interfaz de usuario]. Esta es una preocupación legítima, porque saben que es muy difícil escribir código que sea robusto ante la reentrada ".
Sí lo es. Muy difícil de escribir código que sea robusto ante la reentrada. Y los bucles de mensajes anidados te obligan a escribir código que sea robusto frente a la reentrada. Esta es la razón por la que la respuesta aceptada (y la más votada) para esta pregunta es extremadamente peligrosa en la práctica.
Si está completamente fuera de todas las demás opciones: no puede rediseñar su código, no puede reestructurarlo para que sea asíncrono, se ve obligado a sincronizar el código de llamada inalterable, no puede cambiar el código de abajo para que se sincronice - no puede bloquear - no puede ejecutar el código asíncrono en un subproceso separado - entonces y solo entonces debería considerar abrazar la reentrada.
Si se encuentra en este rincón, le recomendaría usar algo como Dispatcher.PushFrame
para las aplicaciones WPF , hacer un bucle con las Application.DoEvents
para WinForm, y para el caso general, mi propio AsyncContext.Run
.
Estoy aprendiendo sobre async / await, y me encontré con una situación en la que necesito llamar un método asíncrono de forma síncrona. ¿Cómo puedo hacer eso?
Método asíncrono:
public async Task<Customers> GetCustomers()
{
return await Service.GetCustomersAsync();
}
Uso normal:
public async void GetCustomers()
{
customerList = await GetCustomers();
}
He intentado usar lo siguiente:
Task<Customer> task = GetCustomers();
task.Wait()
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Task<Customer> task = GetCustomers();
while(task.Status != TaskStatus.RanToCompletion)
También intenté una sugerencia desde here , sin embargo, no funciona cuando el operador está en estado suspendido.
public static void WaitWithPumping(this Task task)
{
if (task == null) throw new ArgumentNullException(“task”);
var nestedFrame = new DispatcherFrame();
task.ContinueWith(_ => nestedFrame.Continue = false);
Dispatcher.PushFrame(nestedFrame);
task.Wait();
}
Aquí está la excepción y el seguimiento de la pila de llamar a RunSynchronously
:
System.InvalidOperationException
Mensaje : RunSynchronously no se puede llamar en una tarea no vinculada a un delegado.
InnerException : null
Fuente : mscorlib
StackTrace :
at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
at System.Threading.Tasks.Task.RunSynchronously()
at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:/Documents and Settings/.../MyApplication.CustomControls/Controls/MyCustomControl.xaml.cs:line 638
at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:/Documents and Settings/.../MyApplication.CustomControls/Controls/MyCustomControl.xaml.cs:line 233
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:/Documents and Settings/.../MyApplication.CustomControls/Controls/MyCustomControl.xaml.cs:line 597
at System.Collections.Generic.List`1.ForEach(Action`1 action)
at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:/Documents and Settings/.../MyApplication.CustomControls/Controls/MyCustomControl.xaml.cs:line 625
at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()
at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Threading.DispatcherOperation.Invoke()
at System.Windows.Threading.Dispatcher.ProcessQueue()
at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
at System.Windows.Threading.Dispatcher.Run()
at System.Windows.Application.RunDispatcher(Object ignore)
at System.Windows.Application.RunInternal(Window window)
at System.Windows.Application.Run(Window window)
at System.Windows.Application.Run()
at MyApplication.App.Main() in C:/Documents and Settings/.../MyApplication/obj/Debug/App.g.cs:line 50
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Aquí hay una solución que encontré que funciona para todos los casos (incluidos los despachadores suspendidos). No es mi código y todavía estoy trabajando para entenderlo completamente, pero funciona.
Se puede llamar usando:
customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());
El código es de here
public static class AsyncHelpers
{
/// <summary>
/// Execute''s an async Task<T> method which has a void return value synchronously
/// </summary>
/// <param name="task">Task<T> method to execute</param>
public static void RunSync(Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
synch.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Execute''s an async Task<T> method which has a T return type synchronously
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static T RunSync<T>(Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
T ret = default(T);
synch.Post(async _ =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
public override void Post(SendOrPostCallback d, object state)
{
lock (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
public void EndMessageLoop()
{
Post(_ => done = true, null);
}
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > 0)
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
En su código, su primera espera para que se ejecute la tarea, pero no la ha iniciado, por lo que espera indefinidamente. Prueba esto:
Task<Customer> task = GetCustomers();
task.RunSynchronously();
Editar:
Usted dice que obtiene una excepción. Por favor, publicar más detalles, incluyendo el seguimiento de la pila.
Mono contains el siguiente caso de prueba:
[Test]
public void ExecuteSynchronouslyTest ()
{
var val = 0;
Task t = new Task (() => { Thread.Sleep (100); val = 1; });
t.RunSynchronously ();
Assert.AreEqual (1, val);
}
Compruebe si esto funciona para usted. Si no es así, aunque es muy poco probable, es posible que tenga una compilación impar de CTP asíncrono. Si funciona, es posible que desee examinar qué genera exactamente el compilador y cómo Task
creación de instancias de Task
es diferente de esta muestra.
Edición # 2:
Verifiqué con Reflector que la excepción que describiste ocurre cuando m_action
es null
. Esto es un poco extraño, pero no soy un experto en CTP asíncrono. Como dije, debes descompilar tu código y ver cómo Task
se está instanciando exactamente cómo m_action
es null
.
PD: ¿Cuál es el trato con los downvotes ocasionales? ¿Cuidado para elaborar?
Encontré este código en el componente Microsoft.AspNet.Identity.Core, y funciona.
private static readonly TaskFactory _myTaskFactory = new
TaskFactory(CancellationToken.None, TaskCreationOptions.None,
TaskContinuationOptions.None, TaskScheduler.Default);
// Microsoft.AspNet.Identity.AsyncHelper
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
CultureInfo cultureUi = CultureInfo.CurrentUICulture;
CultureInfo culture = CultureInfo.CurrentCulture;
return AsyncHelper._myTaskFactory.StartNew<Task<TResult>>(delegate
{
Thread.CurrentThread.CurrentCulture = culture;
Thread.CurrentThread.CurrentUICulture = cultureUi;
return func();
}).Unwrap<TResult>().GetAwaiter().GetResult();
}
Es mucho más sencillo ejecutar la tarea en el grupo de subprocesos, en lugar de intentar engañar al programador para que lo ejecute de forma síncrona. De esa manera, puedes estar seguro de que no se bloqueará. El rendimiento se ve afectado por el cambio de contexto.
Task<MyResult> DoSomethingAsync() { ... }
// Starts the asynchronous task on a thread-pool thread.
// Returns a proxy to the original task.
Task<MyResult> task = Task.Run(() => DoSomethingAsync());
// Will block until the task is completed...
MyResult result = task.Result;
Esto esta funcionando bien para mi
public static class TaskHelper
{
public static void RunTaskSynchronously(this Task t)
{
var task = Task.Run(async () => await t);
task.Wait();
}
public static T RunTaskSynchronously<T>(this Task<T> t)
{
T res = default(T);
var task = Task.Run(async () => res = await t);
task.Wait();
return res;
}
}
La forma más sencilla que he encontrado de ejecutar la tarea de forma síncrona y sin bloquear el subproceso de la interfaz de usuario es usar Ejecutar de forma síncrona () como:
Task t = new Task(() =>
{
//.... YOUR CODE ....
});
t.RunSynchronously();
En mi caso, tengo un evento que se dispara cuando ocurre algo. No sé cuántas veces ocurrirá. Por lo tanto, uso el código anterior en mi evento, así que cada vez que se dispara, crea una tarea. Las tareas se ejecutan de forma síncrona y funciona muy bien para mí. Me sorprendió que me haya costado tanto descubrirlo, considerando lo simple que es. Por lo general, las recomendaciones son mucho más complejas y propensas a errores. Esto fue simple y limpio.
Lo he enfrentado varias veces, principalmente en pruebas de unidad o en el desarrollo de un servicio de Windows. Actualmente siempre uso esta característica:
var runSync = Task.Factory.StartNew(new Func<Task>(async () =>
{
Trace.WriteLine("Task runSync Start");
await TaskEx.Delay(2000); // Simulates a method that returns a task and
// inside it is possible that there
// async keywords or anothers tasks
Trace.WriteLine("Task runSync Completed");
})).Unwrap();
Trace.WriteLine("Before runSync Wait");
runSync.Wait();
Trace.WriteLine("After runSync Waited");
Es simple, fácil y no tuve problemas.
Si estoy leyendo su pregunta correctamente, el código que desea que la llamada síncrona a un método asíncrono se ejecute en un subproceso de distribuidor suspendido. Y desea bloquear ese hilo de forma síncrona hasta que se complete el método asíncrono.
Los métodos asíncronos en C # 5 se activan al cortar efectivamente el método en pedazos debajo del capó y al devolver una Task
que puede rastrear la finalización general de todo el shabang. Sin embargo, la forma en que se ejecutan los métodos recortados puede depender del tipo de expresión que se pase al operador de await
.
La mayoría de las veces, estará utilizando la await
en una expresión de tipo Task
. La implementación de la tarea del patrón de await
es "inteligente", ya que difiere al SynchronizationContext
, que básicamente hace que suceda lo siguiente:
- Si el subproceso que entra en la
await
está en un subproceso de bucle de mensajes de Dispatcher o WinForms, garantiza que los fragmentos del método asíncrono se produzcan como parte del procesamiento de la cola de mensajes. - Si el subproceso que entra en la
await
está en un subproceso de conjunto de subprocesos, los fragmentos restantes del método asíncrono se producen en cualquier lugar del conjunto de subprocesos.
Es por eso que probablemente se encuentre con problemas (la implementación del método asíncrono está intentando ejecutar el resto en el Dispatcher) aunque esté suspendido.
.... retrocediendo! ....
Tengo que hacer la pregunta, ¿por qué intenta bloquear de forma síncrona en un método asíncrono? Hacer esto anularía el propósito de por qué el método quería llamarse de forma asíncrona. En general, cuando empiece a usar await
en un Dispatcher o un método de UI, querrá convertir todo su flujo de UI en asíncrono. Por ejemplo, si su pila de llamadas fue algo como lo siguiente:
- [Arriba]
WebRequest.GetResponse()
-
YourCode.HelperMethod()
-
YourCode.AnotherMethod()
-
YourCode.EventHandlerMethod()
-
[UI Code].Plumbing()
- CódigoWPF
oWinForms
- [Message Loop] -
WPF
oWinForms
Message Loop
Luego, una vez que el código se ha transformado para usar async, normalmente terminará con
- [Arriba]
WebRequest.GetResponseAsync()
-
YourCode.HelperMethodAsync()
-
YourCode.AnotherMethodAsync()
-
YourCode.EventHandlerMethodAsync()
-
[UI Code].Plumbing()
- CódigoWPF
oWinForms
- [Message Loop] -
WPF
oWinForms
Message Loop
En realidad respondiendo
La clase AsyncHelpers anterior realmente funciona porque se comporta como un bucle de mensaje anidado, pero instala su propio mecanismo paralelo en el Dispatcher en lugar de intentar ejecutarlo en el Dispatcher. Esa es una solución para su problema.
Otra solución es ejecutar el método asíncrono en un subproceso de subprocesos y luego esperar a que se complete. Hacerlo es fácil, puede hacerlo con el siguiente fragmento de código:
var customerList = TaskEx.RunEx(GetCustomers).Result;
La API final será Task.Run (...), pero con el CTP necesitará los sufijos Ex ( explicación aquí ).
Sorprendido nadie mencionó esto:
public Task<int> BlahAsync()
{
// ...
}
int result = BlahAsync().GetAwaiter().GetResult();
No es tan bonito como algunos de los otros métodos aquí, pero tiene los siguientes beneficios:
- no traga excepciones (como
Wait
) - no envolverá ninguna excepción lanzada en una excepción
AggregateException
(comoResult
) - funciona tanto para la
Task
como para laTask<T>
(¡ pruébelo usted mismo! )
Además, dado que GetAwaiter
está GetAwaiter
pato, esto debería funcionar para cualquier objeto que se devuelva desde un método asíncrono (como ConfiguredAwaitable
o YieldAwaitable
), no solo Tareas.
edición: tenga en cuenta que es posible que este enfoque (o el uso de .Result
) se .Result
en un interbloqueo, a menos que se asegure de agregar .ConfigureAwait(false)
cada vez que espere, para todos los métodos asíncronos a los que se pueda acceder desde BlahAsync()
(no solo unos a los que llama directamente). Explanation
// In BlahAsync() body
await FooAsync(); // BAD!
await FooAsync().ConfigureAwait(false); // Good... but make sure FooAsync() and
// all its descendants use ConfigureAwait(false)
// too. Then you can be sure that
// BlahAsync().GetAwaiter().GetResult()
// won''t deadlock.
Si eres demasiado perezoso para agregar .ConfigureAwait(false)
todas partes, y no te importa el rendimiento, puedes hacerlo alternativamente
Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
¿Por qué no crear una llamada como:
Service.GetCustomers();
Eso no es asíncrono.
Ten en cuenta que esta respuesta tiene tres años. Lo escribí basado principalmente en una experiencia con .Net 4.0, y muy poco con 4.5, especialmente con async-await
. En general, es una buena solución simple, pero a veces rompe cosas. Por favor lea la discusión en los comentarios.
.Net 4.5
Solo usa esto:
// For Task<T>: will block until the task is completed...
var result = task.Result;
// For Task (not Task<T>): will block until the task is completed...
task2.RunSynchronously();
Ver: TaskAwaiter , Task.Result , Task.RunSynchronously
.Net 4.0
Utilizar esta:
var x = (IAsyncResult)task;
task.Start();
x.AsyncWaitHandle.WaitOne();
...o esto:
task.Start();
task.Wait();
Creo que el siguiente método de ayuda también podría resolver el problema.
private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
{
TResult result = default(TResult);
var autoResetEvent = new AutoResetEvent(false);
Task.Run(async () =>
{
try
{
result = await func();
}
catch (Exception exc)
{
mErrorLogger.LogError(exc.ToString());
}
finally
{
autoResetEvent.Set();
}
});
autoResetEvent.WaitOne();
return result;
}
Se puede utilizar de la siguiente manera:
InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
En wp8:
Envolverlo:
Task GetCustomersSynchronously()
{
Task t = new Task(async () =>
{
myCustomers = await GetCustomers();
}
t.RunSynchronously();
}
Llámalo:
GetCustomersSynchronously();
Esta respuesta está diseñada para cualquier persona que use WPF para .NET 4.5.
Si intenta ejecutar Task.Run()
en el subproceso de la GUI, task.Wait()
se bloqueará indefinidamente, si no tiene la async
palabra clave en la definición de su función.
Este método de extensión resuelve el problema al verificar si estamos en el subproceso de la GUI y, de ser así, ejecutar la tarea en el subproceso del distribuidor de WPF.
Esta clase puede actuar como el pegamento entre el mundo async / await y el mundo no async / await, en situaciones donde es inevitable, como las propiedades MVVM o las dependencias de otras API que no usan async / await.
/// <summary>
/// Intent: runs an async/await task synchronously. Designed for use with WPF.
/// Normally, under WPF, if task.Wait() is executed on the GUI thread without async
/// in the function signature, it will hang with a threading deadlock, this class
/// solves that problem.
/// </summary>
public static class TaskHelper
{
public static void MyRunTaskSynchronously(this Task task)
{
if (MyIfWpfDispatcherThread)
{
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E99213. Task did not run to completion.");
}
}
else
{
task.Wait();
if (task.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E33213. Task did not run to completion.");
}
}
}
public static T MyRunTaskSynchronously<T>(this Task<T> task)
{
if (MyIfWpfDispatcherThread)
{
T res = default(T);
var result = Dispatcher.CurrentDispatcher.InvokeAsync(async () => { res = await task; });
result.Wait();
if (result.Status != DispatcherOperationStatus.Completed)
{
throw new Exception("Error E89213. Task did not run to completion.");
}
return res;
}
else
{
T res = default(T);
var result = Task.Run(async () => res = await task);
result.Wait();
if (result.Status != TaskStatus.RanToCompletion)
{
throw new Exception("Error E12823. Task did not run to completion.");
}
return res;
}
}
/// <summary>
/// If the task is running on the WPF dispatcher thread.
/// </summary>
public static bool MyIfWpfDispatcherThread
{
get
{
return Application.Current.Dispatcher.CheckAccess();
}
}
}
Esto me funciona
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp2
{
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
class SomeClass
{
public async Task<object> LoginAsync(object loginInfo)
{
return await Task.FromResult(0);
}
public object Login(object loginInfo)
{
return AsyncHelper.RunSync(() => LoginAsync(loginInfo));
//return this.LoginAsync(loginInfo).Result.Content;
}
}
class Program
{
static void Main(string[] args)
{
var someClass = new SomeClass();
Console.WriteLine(someClass.Login(1));
Console.ReadLine();
}
}
}
Intenta seguir el código que me funciona:
public async void TaskSearchOnTaskList (SearchModel searchModel)
{
try
{
List<EventsTasksModel> taskSearchList = await Task.Run(
() => MakeasyncSearchRequest(searchModel),
cancelTaskSearchToken.Token);
if (cancelTaskSearchToken.IsCancellationRequested
|| string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
{
return;
}
if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
{
RunOnUiThread(() => {
textViewNoMembers.Visibility = ViewStates.Visible;
taskListView.Visibility = ViewStates.Gone;
});
taskSearchRecureList = null;
return;
}
else
{
taskSearchRecureList = TaskFooterServiceLayer
.GetRecurringEvent(taskSearchList);
this.SetOnAdapter(taskSearchRecureList);
}
}
catch (Exception ex)
{
Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
}
}
O simplemente puedes ir con:
customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;
Para compilar esto, asegúrese de hacer referencia al ensamble de extensión:
System.Net.Http.Formatting
Solo una pequeña nota - este enfoque:
Task<Customer> task = GetCustomers();
task.Wait()
Trabaja para WinRT.
Dejame explicar:
private void TestMethod()
{
Task<Customer> task = GetCustomers(); // call async method as sync and get task as result
task.Wait(); // wait executing the method
var customer = task.Result; // get''s result.
Debug.WriteLine(customer.Name); //print customer name
}
public class Customer
{
public Customer()
{
new ManualResetEvent(false).WaitOne(TimeSpan.FromSeconds(5));//wait 5 second (long term operation)
}
public string Name { get; set; }
}
private Task<Customer> GetCustomers()
{
return Task.Run(() => new Customer
{
Name = "MyName"
});
}
Además, este enfoque funciona solo para las soluciones de la Tienda Windows.
Nota: esta forma no es segura para subprocesos si llama a su método dentro de otro método asíncrono (según los comentarios de @Servy)
use el código siguiente
Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Probado en .Net 4.6. También puede evitar el interbloqueo.
Para el retorno de un método aysnc Task
.
Task DoSomeWork()
Task.Run(async () => await DoSomeWork()).Wait();
Para el retorno del método asíncrono. Task<T>
Task<T> GetSomeValue()
var result = Task.Run(() => GetSomeValue()).Result;
Puedes usar CoRoutines . Ver la implementación de Caliburn.Micro . Tengo una implementación personalizada here .
Simplemente llamar .Result;
o .Wait()
es un riesgo de interbloqueos como muchos han dicho en los comentarios. Como a la mayoría de nosotros nos gustan los oneliners, puedes usarlos para.Net 4.5<
Adquiriendo un valor a través de un método asíncrono:
var result = Task.Run(() => asyncGetValue()).Result;
Llamando sincrónicamente a un método asíncrono.
Task.Run(() => asyncMethod()).Wait();
No se producirán problemas de interbloqueo debido al uso de Task.Run
.
Fuente:
https://.com/a/32429753/3850405
private int GetSync()
{
try
{
ManualResetEvent mre = new ManualResetEvent(false);
int result = null;
Parallel.Invoke(async () =>
{
result = await SomeCalcAsync(5+5);
mre.Set();
});
mre.WaitOne();
return result;
}
catch (Exception)
{
return null;
}
}