Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would I run an async Task<T> method synchronously?

I am learning about async/await, and ran into a situation where I need to call an async method synchronously. How can I do that?

Async method:

public async Task<Customers> GetCustomers() {     return await Service.GetCustomersAsync(); } 

Normal usage:

public async void GetCustomers() {     customerList = await GetCustomers(); } 

I've tried using the following:

Task<Customer> task = GetCustomers(); task.Wait()  Task<Customer> task = GetCustomers(); task.RunSynchronously();  Task<Customer> task = GetCustomers(); while(task.Status != TaskStatus.RanToCompletion) 

I also tried a suggestion from here, however it doesn't work when the dispatcher is in a suspended state.

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

Here is the exception and stack trace from calling RunSynchronously:

System.InvalidOperationException

Message: RunSynchronously may not be called on a task unbound to a delegate.

InnerException: null

Source: 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() 
like image 295
Rachel Avatar asked Feb 23 '11 18:02

Rachel


People also ask

Does async await make it synchronous?

Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.

Do async tasks run in parallel?

There is no parallelism here, as the “async Task” does not automatically make something run in in parallel. This will spawn 2 threads, run them simultaneously, and return when both threads are done. This will create a list of Tasks to be run at the same time.


1 Answers

Here's a workaround I found that works for all cases (including suspended dispatchers). It's not my code and I'm still working to fully understand it, but it does work.

It can be called using:

customerList = AsyncHelpers.RunSync<List<Customer>>(() => GetCustomers());

Code is from 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;         }     } } 
like image 176
Rachel Avatar answered Sep 29 '22 13:09

Rachel