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

22

760

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()
Lustihood answered 23/2, 2011 at 18:18 Comment(14)
The best answer to the question "How can I call an async method synchronously" is "don't". There are hacks to try to force it to work, but they all have very subtle pitfalls. Instead, back up and fix the code that makes you "need" to do this.Boydboyden
@Stephen Cleary Absolutely agree, but sometimes its simply unavoidable, such as when your code is dependent on some 3rd party API that does not use async/await. In addition, if binding to WPF properties when using MVVM, its literally impossible to use async/await as this is not supported on properties.Feces
@StephenCleary Not always. I'm building a DLL which will be imported in GeneXus. It don't support async/await keywords, so I must use only synchronous methods.Madagascar
@DineiA.Rockenbach: Three better alternatives spring immediately to mind: 1) Have GeneXus add support for async methods; 2) Implement a callback/event instead of using Task (often required for cross-language interop); 3) Use synchronous code all the way, so sync-over-async doesn't even come up. Each of these is a better alternative to using dubious hacks.Boydboyden
@StephenCleary 1) GeneXus is a 3rd pt tool and I don't have access to its source code; 2) GeneXus don't even has implementations of "functions", so I can't realize how I could implement a "callback" with this type of thing. Surely it would be a harder workaround than using Task synchronously; 3) I'm integrating GeneXus with MongoDB C# driver, which expose some methods only asynchronouslyMadagascar
@StephenCleary That's all nice theory, but "don't do it" has that inherent issue with it that it "doesn't work". C# actively forbids me to use await within synchronised blocks. Should I have Microsoft change their language? Or should I drop my synchronisation and accept messed up data structures? async is that cancer, not so much GPL. Once you have it you can't get rid of it.Waterbuck
@ygoe: Use an async-compatible lock, such as SemaphoreSlim.Boydboyden
The absence of StackOverflow the biggest names even in late 2016 strongly suggests that there still isn't a simple method to do that. BTW. I wonder why there is no runsync operator.Kumasi
Possible duplicate of How to call asynchronous method from synchronous method in C#?Gnosticism
Linking a related q/a on how to do this on a UI thread.Volitive
@StephenCleary why .net does not provide something like ".RunAsSynchronous()" method that we can call on Task type ? this kind of method would just ignore/remove all that async stuff and run it. So instead of us writing async method and synchronous versions of it, we could just have one metod but caller would decide if they want to use async stuff on it or just completely ignore it as it never existed.Simoniac
@IronHide: What about code that returns task objects not created by async?Boydboyden
@StephenCleary I have no idea, I did not think of every possible scenario, I assumed that some async gurus like your self will figure it out in like no time :) (Not being sarcastic)Simoniac
I've been told not to use var x = SomeAsyncMethod().Result to run async task inside an non-async method since it will block the main thread and cause deadlock. Is it still valid in 2022 for .NET 6 apps? what if I have that line in a .NET Framework 4.7 project? I've seen people suggest var x = Task.Run( async ()=> await SomeAsyncMethod()).Result instead to avoid deadlock. Will that work?Takao
L
538

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>
    /// Synchronously execute's an async Task method which has a void return value.
    /// </summary>
    /// <param name="task">The Task method to execute.</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        
        syncContext.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Synchronously execute's an async Task<T> method which has a T return type.
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">The Task<T> method to execute.</param>
    /// <returns>The result of awaiting the given Task<T>.</returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var syncContext = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(syncContext);
        T result;
        
        syncContext.Post(async _ =>
        {
            try
            {
                result = await task();
            }
            catch (Exception e)
            {
                syncContext.InnerException = e;
                throw;
            }
            finally
            {
                syncContext.EndMessageLoop();
            }
        }, null);
        
        syncContext.BeginMessageLoop();
        
        SynchronizationContext.SetSynchronizationContext(oldContext);
        
        return result;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        private readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();
        private bool done;
        
        public Exception InnerException { get; set; }

        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;
        }
    }
}
Lustihood answered 23/2, 2011 at 21:2 Comment(22)
For some background on how this works, Stephen Toub (Mr Parallel) wrote a series of posts about this. Part 1 Part 2 Part 3Mydriasis
I updated John's code to work without wrapping tasks in lambdas: github.com/tejacques/AsyncBridge. Essentially you work with async blocks with the using statement. Anything inside a using block happens asynchronously, with a wait at the end. The downside is that you need to unwrap the task yourself in a callback, but it's still fairly elegant, especially if you need to call several async functions at once.Sasaki
This method blocks the UI thread it is executed on. Would it be possible to somehow use Dispatcher.PushFrame(DispatcherFrame) method to prevent blocking the UI?Postwar
@Postwar - if you don't want to block the UI then you can't run it synchronously...by definition it will block the thread it runs on. Just await it if you don't want it to block. I don't understand the point of what you are asking.Chancery
@MikeMarynowski I had a problem with async void (cannot change to Task) method with awaits, which I wanted to execute fully before passing control. This solution helps, but it hangs the UI thread, which I was trying to avoid.Postwar
What you are asking for is impossible though, just think about it - if you don't pass control back to the UI thread then the UI won't update. By definition, something running synchronously on the UI thread will freeze the UI until it finishes. I'm certain there's a better way to achieve whatever it is you are trying to do. Instead of bloating this thread more, how about you post a separate question and I will try my best to answer it.Chancery
@MikeMarynowski It was possible in 4.0 with Dispatcher.PushFrame and it worked quite well. It just so happens it causes async/await code to deadlock. I don't need new question - I just reorganized my app so it didn't need this.Postwar
@StephenCleary Although I generally agree with you that the code should be async all the way down, sometimes you find yourself in an infeasible situation where one has to force it as a synchronous call. Basically, my situation is that all my data access code is in async fashion. I needed to build a sitemap based on the sitemap and the third party library I was using was MvcSitemap. Now when one is extending it via the DynamicNodeProviderBase base class, one cannot declare it as a async method. Either I had to replace with a new library, or just call a synchronous op.Immoral
@justin.lovell: Yes, library limitations can force us to put in hacks, at least until the library is updated. It sounds like MvcSitemap is one such situation where a hack is required (MVC filters and child actions, too); I just dissuade people from this in general because hacks like this are used way too often when they are not necessary. With MVC in particular, some ASP.NET/MVC APIs do assume that they have an AspNetSynchronizationContext, so this particular hack won't work if you're calling those APIs.Boydboyden
@Postwar sure, it still works perfectly: gist.github.com/ivan-danilov/3ee430522d596ccf6496Regent
This code will not work. If it is called from a pool thread it can trigger thread-starvation deadlock. Your caller will block waiting for the operation to complete, which may never happen if he has exhausted the thread pool. See this article.Poco
@Paval This answer is pretty old, however in the past I never had any problems like that with the code. The use of the await keyword says "queue this command on a separate thread, and everything below this to run upon thread completion, then end this code block and go back to whatever else you were doing", which in this case is the message loop calling .WaitOne repeatedly. Because the task runs on a separate thread, I don't think you should experiencing any dead locks with this code.Lustihood
Sometimes you inherit someone else's code that was written all sync, and you have to deliver something by a certain deadline, and rewriting everything to be async upwards isn't an option.Silicify
This code has a bug. It doesn't restore the old context (SynchronizationContext.SetSynchronizationContext(oldContext)) when an exception is thrown. Using try { ... } finally { ... } fixes it.Carpetbagger
To avoid FxCopy warning Implement IDisposable in ExclusiveSynchronizationContext, disposing _workItemsWaitingPeterpeterborough
Unfortunately it does not work for all cases. WaitOne() internally pumps messages (so that COM things work as expected I believe). This means that external code (such as event handlers) can execute every time a task is awaited in the custom context, and WaitOne() is called. I experienced such a situation causing a deadlock between the main thread and itself :/ I solved it by executing my task on a new thread, and actually blocking with a while (!task.IsCompleted) { /*wait*/}. This is ugly, but unless something in your task tries to execute on the waiting thread, everything should work.Goethite
@TomJacques tried your library since I was suspicious of that working without having to wrap in lambdas. Tried with a small winforms app. Didnt work for me. Of course I tested against a hot task.Publisher
I am having issues with my console application just hanging, not sure why but after using this code it started happening.Philipson
Though this answer has been posted a very long time ago. Thank you so much, after series of searches, this worked once.Gilgilba
In some rare scenarios the ExclusiveSynchronizationContext.Post() can be called after leaving BeginMessageLoop() and the callback will never be executed. Example: AsyncHelpers.RunSync(() => MyTask()); private static async Task MyTask() { await Task.Delay(1); var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); Task.Run(async () => await Task.Delay(1000)).ContinueWith(g => { Console.WriteLine("Will never be executed"); }, scheduler); await Task.Delay(100); }Transmogrify
@cameron macfarland cited a blog post by Stephen Toub. It does not work any more. Has someone an updated link?Manufacturer
See Stephen Toub's blog with AsyncPump.Manufacturer
A
367

Be advised this answer is three years old. I wrote it based mostly on a experience with .Net 4.0, and very little with 4.5 especially with async-await. Generally speaking it's a nice simple solution, but it sometimes breaks things. Please read the discussion in the comments.

.Net 4.5

Just use this:

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

See: TaskAwaiter, Task.Result, Task.RunSynchronously


.Net 4.0

Use this:

var x = (IAsyncResult)task;
task.Start();

x.AsyncWaitHandle.WaitOne();

...or this:

task.Start();
task.Wait();
Affirmative answered 2/8, 2012 at 17:3 Comment(13)
If you want to use async in .NET 4.0 you can install the NuGet package async: nuget.org/packages/Microsoft.Bcl.AsyncBoycott
.Result can produce a deadlock in certain scenario'sCandancecandela
@JordyLangen of course, you are waiting for that task to complete... But that's usual with parallel programming, and always true when waiting on something...Affirmative
Result can easily cause deadlock in async code, as I describe on my blog.Boydboyden
@StephenCleary I read your post, and tried it myself. I honestly think someone at microsoft was really drunk... It's the same issue as like winforms and background threads....Affirmative
task.Wait() sometimes does not work "synchronously". This introduces some very nasty bugsAllopath
The question concerns a Task that is returned by async method. Such kind of Task may have already been started, executed, or canceled, so usage of Task.RunSynchronously method may result in InvalidOperationException. See MSDN page: Task.RunSynchronously Method. Besides, that Task is probably created by Task.Factory.StartNew or Task.Run methods (inside async method), so it's dangerous to try start it again. Some race conditions may occur at runtime. In the othe hand, Task.Wait and Task.Result may result i deadlock.Deterrence
Run Synchronously worked for me... I don't know if I'm missing something but this seems preferable to the horrors of the marked answer - I was just looking for a way of switching off async for testing code that just there to stop the ui from hangingActive
Result causes weird connection exceptions in Entity FrameworkYiyid
When you run this the following error occurs : RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.Almemar
The documentation for RunSynchronously() suggests calling Wait() afterword "to handle any exceptions that the task might throw".Centurial
It worth to mention that based on this and this, the deadlock is not a problem for ASP.NET Core!Barmy
a. RunSynchronously may not be called on a task not bound to a delegate so I wrapped it in a Task(async () => await methodAsync().ConfigureAwait(continueOnCapturedContext: false)) b. Added the .Wait() per the MSDN article Surprise the task started synchronous execution then claimed completion when I/O started, leading to baffling race condition. Fell back to .GetAwaiter().GetResult(); which really waited for completion. By why is RunSynchronously() and Wait() returning before async task is really done???Expansive
B
194

Surprised no one mentioned this:

public Task<int> BlahAsync()
{
    // ...
}

int result = BlahAsync().GetAwaiter().GetResult();

Not as pretty as some of the other methods here, but it has the following benefits:

  • it doesn't swallow exceptions (like Wait)
  • it won't wrap any exceptions thrown in an AggregateException (like Result)
  • works for both Task and Task<T> (try it out yourself!)

Also, since GetAwaiter is duck-typed, this should work for any object that is returned from an async method (like ConfiguredAwaitable or YieldAwaitable), not just Tasks.


edit: Please note that it's possible for this approach (or using .Result) to deadlock, unless you make sure to add .ConfigureAwait(false) every time you await, for all async methods that can possibly be reached from BlahAsync() (not just ones it calls directly). 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.

If you're too lazy to add .ConfigureAwait(false) everywhere, and you don't care about performance you can alternatively do

Task.Run(() => BlahAsync()).GetAwaiter().GetResult()
Brandt answered 7/2, 2016 at 19:29 Comment(9)
Works for me for simple stuff. Also, if the method returns an IAsyncOperation, I had to convert it to a Task first: BlahAsync().AsTask().GetAwaiter().GetResult();Inglenook
This caused a deadlock inside an asmx web method. Nevertheless, wrapping the method call in a Task.Run() made it work: Task.Run(() => BlahAsync()).GetAwaiter().GetResult()Vories
I like this approach best syntactically because it does not involve lambdas.Rafaelrafaela
Please do NOT edit other people's answers to insert a link to your own. If you believe your answer is better, leave it as a comment instead.Lustihood
Should be a solution for the question as it addresses the problem, minimal custom code, and answer perfectly explains key points.Charades
learn.microsoft.com/en-us/dotnet/api/… says about GetAwaiter(), "This method is intended for compiler user rather than use directly in code."Obau
This way (BlahAsync()).GetAwaiter().GetResult()) can cause the program to stall and never finish. This is why I had to switch to Rachel's for much better performance.Bigley
much cleaner than accepted, thanks, should remembered :DVedic
Put the 'too lazy' answer nearer to the top! XScalable
R
81

It's much simpler to run the task on the thread pool, rather than trying to trick the scheduler to run it synchronously. That way you can be sure that it won't deadlock. Performance is affected because of the context switch.

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; 
Remmer answered 13/6, 2013 at 18:50 Comment(6)
And if the task is void (without result)?Sassenach
Then you call task.Wait(). The data type is simply Task.Remmer
Let's assume that DoSomethingAsync() is long-running async method as whole (internally it awaits a long-running task), but it yields back a flow control to its caller quickly, thus the lambda argument work ends also quickly. The result of Tusk.Run() may Task<Task> or Task<Task<>>, so you are awaiting a result of outer task which is completed quickly, but inner task ( due to awaiting long-running job in async method) is still running. Conclusions are that we probably need to use Unwrap() approach (as was done in @J.Lennon post) to achieve synchronous behaviour of async method.Deterrence
@Deterrence You are wrong. Task.Run is different than Task.Factory.StartNew in that it automatically unwraps the result already. See this article.Poco
Can I just write Task.Run(DoSomethingAsync) instead? This removes one level of delegates.Waterbuck
Yep. Going the opposite direction, though, as in Task<MyResult> task = Task.Run(async () => await DoSomethingAsync()); is more explicit and addresses the concern by @Deterrence that it might be returning a Task<Task<MyResult>>. The correct overload of Task.Run is selected either way, but the async delegate makes your intent obvious.Remmer
B
73

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

The best answer is you don't, with the details dependent on what the "situation" is.

Is it a property getter/setter? In most cases, it's better to have asynchronous methods than "asynchronous properties". (For more info, see my blog post on asynchronous properties).

Is this an MVVM app and you want to do asynchronous data binding? Then use something like my NotifyTask, as described in my MSDN article on asynchronous data binding.

Is it a constructor? Then you probably want to consider an asynchronous factory method. (For more info, see my blog post on asynchronous constructors).

There's almost always a better answer than to do sync-over-async.

If it's not possible for your situation (and you know this by asking a question here describing the situation), then I'd recommend just using synchronous code. Async all the way is best; sync all the way is second-best. Sync-over-async is not recommended.

However, there are a handful of situations where sync-over-async is necessary. Specifically, you are constrained by the calling code so that you have to be sync (and have absolutely no way to re-think or re-structure your code to allow asynchrony), and you have to call async code. This is a very rare situation, but it does come up from time to time.

In that case, you would need to use one of the hacks described in my article on brownfield async development, specifically:

  • Blocking (e.g., GetAwaiter().GetResult()). Note that this can cause deadlocks (as I describe on my blog).
  • Running the code on a thread pool thread (e.g., Task.Run(..).GetAwaiter().GetResult()). Note that this will only work if the asynchronous code can be run on a thread pool thread (i.e., is not dependent on a UI or ASP.NET context).
  • Nested message loops. Note that this will only work if the asynchronous code only assumes a single-threaded context, not a specific context type (a lot of UI and ASP.NET code expect a specific context).

Nested message loops are the most dangerous of all the hacks, because it causes re-entrancy. Re-entrancy is extremely tricky to reason about, and (IMO) is the cause of most application bugs on Windows. In particular, if you're on the UI thread and you block on a work queue (waiting for the async work to complete), then the CLR actually does some message pumping for you - it'll actually handle some Win32 messages from within your code. Oh, and you have no idea which messages - when Chris Brumme says "Wouldn’t it be great to know exactly what will get pumped? Unfortunately, pumping is a black art which is beyond mortal comprehension.", then we really have no hope of knowing.

So, when you block like this on a UI thread, you're asking for trouble. Another cbrumme quote from the same article: "From time to time, customers inside or outside the company discover that we are pumping messages during managed blocking on an STA [UI thread]. This is a legitimate concern, because they know that it’s very hard to write code that’s robust in the face of reentrancy."

Yes, it is. Very hard to write code that's robust in the face of reentrancy. And nested message loops force you to write code that's robust in the face of reentrancy. This is why the accepted (and most-upvoted) answer for this question is extremely dangerous in practice.

If you are completely out of all other options - you can't redesign your code, you can't restructure it to be async - you are forced by unchangeable calling code to be sync - you can't change the downstream code to be sync - you can't block - you can't run the async code on a separate thread - then and only then should you consider embracing reentrancy.

If you do find yourself in this corner, I would recommend using something like Dispatcher.PushFrame for WPF apps, looping with Application.DoEvents for WinForm apps, and for the general case, my own AsyncContext.Run.

Boydboyden answered 7/9, 2016 at 13:40 Comment(7)
Stephen, there is another very similar qestion which you provided awesome answer too. Do you think one of them can be closed as duplicate or maybe merge request or bring up on meta first (as each q has ~200K views 200+ votes)? Suggestions?Numbat
@AlexeiLevenkov: I don't feel right doing that, for a few reasons: 1) The answer on the linked question is fairly out-of-date. 2) I've written an entire article on the subject that I feel is more complete than any existing SO Q/A. 3) The accepted answer on this question is extremely popular. 4) I am vehemently opposed to that accepted answer. So, closing this as a dup of that would be an abuse of power; closing that as a dup of this (or merging) would empower a dangerous answer even more. I let it be, and leave it to the community.Boydboyden
Ok. I'll consider bringing it up on meta than in some way.Numbat
This answer goes a long way over my head. "Use async all the way down" is confusing advice, due to clearly not being possible to follow. A program with an async Main() method doesn't compile; at some point you've got to bridge the gap between the sync and async worlds. It's not a "very rare situation", it's necessary in literally every program that calls an async method. There is no option to not "do sync-over-async", just an option to shunt that burden up to the calling method instead of shouldering it in the one you're currently writing.Ambrosio
@MarkAmery: Sync-over-async is necessary in the Main method of console apps. ASP.NET, unit test frameworks, and every UI system all support async natively. Even if all your apps are console apps, you'd only need to do sync-over-async once per app. (of course, library callbacks that don't support async yet may require additional hacks).Boydboyden
Great. I'm about to put async on all of the methods in my application now. And that's a lot. Can't this just be the default?Waterbuck
It worth to mention that as you replied me and based on your blog here, the deadlock is not a problem for ASP.NET Core!Barmy
I
26

If I am reading your question right - the code that wants the synchronous call to an async method is executing on a suspended dispatcher thread. And you want to actually synchronously block that thread until the async method is completed.

Async methods in C# 5 are powered by effectively chopping the method into pieces under the hood, and returning a Task that can track the overall completion of the whole shabang. However, how the chopped up methods execute can depend on the type of the expression passed to the await operator.

Most of the time, you'll be using await on an expression of type Task. Task's implementation of the await pattern is "smart" in that it defers to the SynchronizationContext, which basically causes the following to happen:

  1. If the thread entering the await is on a Dispatcher or WinForms message loop thread, it ensures that the chunks of the async method occurs as part of the processing of the message queue.
  2. If the thread entering the await is on a thread pool thread, then the remaining chunks of the async method occur anywhere on the thread pool.

That's why you're probably running into problems - the async method implementation is trying to run the rest on the Dispatcher - even though it's suspended.

.... backing up! ....

I have to ask the question, why are you trying to synchronously block on an async method? Doing so would defeat the purpose on why the method wanted to be called asynchronously. In general, when you start using await on a Dispatcher or UI method, you will want to turn your entire UI flow async. For example, if your callstack was something like the following:

  1. [Top] WebRequest.GetResponse()
  2. YourCode.HelperMethod()
  3. YourCode.AnotherMethod()
  4. YourCode.EventHandlerMethod()
  5. [UI Code].Plumbing() - WPF or WinForms Code
  6. [Message Loop] - WPF or WinForms Message Loop

Then once the code has been transformed to use async, you'll typically end up with

  1. [Top] WebRequest.GetResponseAsync()
  2. YourCode.HelperMethodAsync()
  3. YourCode.AnotherMethodAsync()
  4. YourCode.EventHandlerMethodAsync()
  5. [UI Code].Plumbing() - WPF or WinForms Code
  6. [Message Loop] - WPF or WinForms Message Loop

Actually Answering

The AsyncHelpers class above actually works because it behaves like a nested message loop, but it installs its own parallel mechanic to the Dispatcher rather than trying to execute on the Dispatcher itself. That's one workaround for your problem.

Another workaround is to execute your async method on a threadpool thread, and then wait for it to complete. Doing so is easy - you can do it with the following snippet:

var customerList = TaskEx.RunEx(GetCustomers).Result;

The final API will be Task.Run(...), but with the CTP you'll need the Ex suffixes (explanation here).

Inductee answered 24/2, 2011 at 21:29 Comment(2)
+1 for the detailed explanation, however TaskEx.RunEx(GetCustomers).Result hangs the application when it gets run on a suspended dispatcher thread. Also, the GetCustomers() method is normally run async, however in one situation it needs to run synchronously, so I was looking for a way to do that without building a sync version of the method.Lustihood
+1 for "why are you trying to synchronously block on an async method?" There is always a way to properly use async methods; nested loops should certainly be avoided.Boydboyden
Z
25

This is working well for me

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;
    }
}
Zuzana answered 23/7, 2014 at 5:42 Comment(4)
You need to also use Task.Unwrap method, because your Task.Wait statement causes waiting for outer Task (created by Task.Run), not for inner await t Task passed as parameter of extension method. Your Task.Run method returns not Task<T>, but Task<Task<T>>. In some simple scenarios your solution may works because of TaskScheduler optimizations, for example using TryExecuteTaskInline method to execute Tasks within current thread during Wait operation .Please look at my comment to this answer.Deterrence
That is not correct. The Task.Run will return Task<T>. See this overload msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspxZuzana
How is this supposed to be used? This deadlocks in WPF: MyAsyncMethod().RunTaskSynchronously();Waterbuck
This only works for platforms without synchronization contexts (console apps, ASP.NET Core apps etc). For platforms with sync context, this work for only cold tasks, aka not the 99% normal cases. For tasks that has already started, there is no point in wrapping it in Task.Run. In other words, in normal usages like GetFromNetworkAsync().RunTaskSynchronously() hangs for UI apps.Publisher
C
23

Tested in .Net 4.6. It can also avoid deadlock.

For async method returning Task.

Task DoSomeWork();
Task.Run(async () => await DoSomeWork()).Wait();

For async method returning Task<T>

Task<T> GetSomeValue();
var result = Task.Run(() => GetSomeValue()).Result;

Edit:

If the caller is running in the thread pool thread (or the caller is also in a task), it may still cause a deadlock in some situation.

Charissacharisse answered 24/10, 2018 at 9:8 Comment(2)
My answar after almost 8 years :) The second example - will produce a deadlock in all scheduled context that are mainly used (console app / .NET core / desktop app / ...). here you have more overview what i'm talking about now: medium.com/rubrikkgroup/…Sprain
Result is perfect for the job if you want a synchronous call, and downright dangerous otherwise. There is nothing in the name Result or in the intellisense of Result that indicates it is a blocking call. It really should be renamed.Spook
S
17

I've faced it a few times, mostly in unit testing or in a windows service development. Currently I always use this feature:

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

It's simple, easy and I had no problems.

Sassenach answered 5/10, 2013 at 2:54 Comment(2)
This is the only one that didn't deadlock for me.Donelladonelle
@Donelladonelle I dont know what it is, but this is essentially Task.Run(() => ..).Wait() (with slight tweaks). Both should work.Publisher
C
17

I found this code at Microsoft.AspNet.Identity.Core component, and it works.

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();
}
Cumings answered 2/12, 2014 at 2:6 Comment(2)
github.com/aspnet/AspNetIdentity/blob/master/src/…Groggery
I wonder why there is a call to Unwrap<TResult> ()?Moitoso
M
15

The simplest way I have found to run task synchronously and without blocking UI thread is to use RunSynchronously() like:

Task t = new Task(() => 
{ 
   //.... YOUR CODE ....
});
t.RunSynchronously();

In my case, I have an event that fires when something occurs. I dont know how many times it will occur. So, I use code above in my event, so whenever it fires, it creates a task. Tasks are executed synchronously and it works great for me. I was just surprised that it took me so long to find out of this considering how simple it is. Usually, recommendations are much more complex and error prone. This was it is simple and clean.

Mascarenas answered 25/10, 2016 at 23:22 Comment(3)
But how could we use this method when the async code returns something we need?Barmy
This works for cold tasks, not tasks that has begun.Publisher
This will not work because "YOUR CODE" in your example must be synchronous code for this Task constructor. But we actually have async code we would want to put there.Manufacturer
M
14

Just a little note - this approach:

Task<Customer> task = GetCustomers();
task.Wait()

works for WinRT.

Let me explain:

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

Moreover this approach works for Windows Store solutions only!

Note: This way isn't thread safe if you call your method inside of other async method (according to comments of @Servy)

Mcglone answered 20/6, 2012 at 22:10 Comment(8)
I explained this solution, check EDIT section.Mcglone
This can very easily result in deadlocks when called in asynchronous situations.Cleavage
@Cleavage make sense. So as I get correct using Wait(timeOut) can help, right?Mcglone
Then you need to worry about having the timeout being reached when the operation isn't actually done, which is very bad, and also the time spent waiting until the timeout in the cases where it deadlocks (and in that case you're still continuing on when it's not done). So no, that doesn't fix the problem.Cleavage
@Cleavage Looks like I have to implement CancellationToken for my solution.Mcglone
@Cleavage Why, could you explain a bit?Mcglone
I could, or you could just ask Google about it, or for that matter just look at the comments on the answers that are, more or less, a duplicate of yours; in particular the one with the most votes, as it has [a link to] an explanation.Cleavage
Your example works because your GetCustomers() method is not the async method in terms of C# 5.0 (no async-await keywords pair), but just a Task-returning method. The question was about C# 5.0 async method. Mark your GetCustomers() method as async, then await Task.Run() invocation, then you will probably get deadlock.Deterrence
C
10

NOTE: I think as best practice is not recommended to change the nature of the action if it is asynchronous the best thing is handling as it is (async all the way). In that way you can get other benefits like parallel processing / multi-threading, etc.

Seeing the other answers did not use this approach, I want to post it here as well:

var customers = GetCustomersAsync().GetAwaiter().GetResult();
Catawba answered 16/11, 2020 at 20:34 Comment(1)
This is still equivalent to calling var customer = GetCustomersAsync().Result but now exceptions are unwrapped instead of being wrapped inside an AggregatedException.Takao
T
7

In your code, your first wait for task to execute but you haven't started it so it waits indefinitely. Try this:

Task<Customer> task = GetCustomers();
task.RunSynchronously();

Edit:

You say that you get an exception. Please post more details, including stack trace.
Mono contains the following test case:

[Test]
public void ExecuteSynchronouslyTest ()
{
        var val = 0;
        Task t = new Task (() => { Thread.Sleep (100); val = 1; });
        t.RunSynchronously ();

        Assert.AreEqual (1, val);
}

Check if this works for you. If it does not, though very unlikely, you might have some odd build of Async CTP. If it does work, you might want to examine what exactly the compiler generates and how Task instantiation is different from this sample.

Edit #2:

I checked with Reflector that the exception you described occurs when m_action is null. This is kinda odd, but I'm no expert on Async CTP. As I said, you should decompile your code and see how exactly Task is being instantiated any how come its m_action is null.

Thrombophlebitis answered 23/2, 2011 at 18:23 Comment(7)
I adjusted my question to make the code I had attempted a bit clearer. RunSynchronously returns an error of RunSynchronously may not be called on a task unbound to a delegate. Google is no help since all the results for that are in chinese...Lustihood
I think the difference is that I don't create the Task and then try to run it. Instead, the task is created by the async method when the await keyword is used. The exception posted in my earlier comment is the exception I get, although it is one of the few that I cannot Google and find a cause or resolution for.Lustihood
async and async keywords are nothing more than syntax sugar. Compiler generates code to create Task<Customer> in GetCustomers() so that's where I would look first. As for exception, you only posted exception message, which is useless without exception type and stack trace. Call exception's ToString() method and post output in the question.Thrombophlebitis
@gaearon: I posted the exception details and stack trace in my original question.Lustihood
I'm also wondering if you're using Task from .NET 4.0 (not some preview release) and stable version of Async CTP.Thrombophlebitis
I'm using the most stable release of AsyncCTP as of a month or two ago, and the .Net 4.0 Task object. I actually found an alternative that works, although its not my code so I'm still working to fully understand it.Lustihood
@gaearon I think you had got downvotes because your post is not applicable to question. The discussion is about async-await methods, not about simple Task-returning methods. Moreover, in my opinion, async-await mechanism is a syntax sugar, but not so trivial - there is continuation , context capturing, local context resuming, enhanced local exceptions handling, and more. Then, you shouldn't invoke RunSynchronously method on result of the async method, because by definition asynchronous method should return Task that is currently at least scheduled, and more than once is in the running state.Deterrence
A
5

Why not create a call like:

Service.GetCustomers();

that isn't async.

Avar answered 23/2, 2011 at 18:20 Comment(2)
That's going to be what I do if I can't get this working... create a Sync version in addition to an Async versionLustihood
What if you cannot write your own functions since you are using a lib?Selfevident
F
3

This answer is designed for anyone who is using WPF for .NET 4.5.

If you attempt to execute Task.Run() on the GUI thread, then task.Wait() will hang indefinitely, if you do not have the async keyword in your function definition.

This extension method solves the problem by checking to see if we are on the GUI thread, and if so, running the task on the WPF dispatcher thread.

This class can act as the glue between the async/await world and the non-async/await world, in situations where it is unavoidable, such as MVVM properties or dependencies on other APIs that do not use 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();
        }
    }
}
Feces answered 12/9, 2014 at 17:41 Comment(0)
L
3

Simply calling .Result; or .Wait() is a risk for deadlocks as many have said in comments. Since most of us like oneliners you can use these for .Net 4.5<

Acquiring a value via an async method:

var result = Task.Run(() => asyncGetValue()).Result;

Syncronously calling an async method

Task.Run(() => asyncMethod()).Wait();

No deadlock issues will occur due to the use of Task.Run.

Source:

https://mcmap.net/q/47495/-39-await-39-works-but-calling-task-result-hangs-deadlocks

Lupe answered 25/9, 2017 at 13:49 Comment(1)
If you vote down please add a comment why. Hard to improve answer otherwiseLupe
E
2

use below code snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));
Exigible answered 19/10, 2016 at 4:13 Comment(1)
Task.WaitAll adds nothing here. Why not just Wait?Publisher
J
2

This is works for me

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();
        }
    }
}
Jin answered 27/7, 2018 at 15:20 Comment(0)
M
1

I think the following helper method could also solve the problem.

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

Can be used the following way:

InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
Mule answered 16/2, 2016 at 9:58 Comment(5)
It is not an true "synchronously".Y ou create two threads and wait in first results of other.Asher
and all things aside, this is a very bad idea.Eunuchize
I just wrote almost the identical code (line by line the same) but instead using SemaphoreSlim instead of the auto reset event. Wish I had seen this sooner. I find this approach to prevent deadlocks and keeps your async code running the same as it does in true asynchronous scenarios. Not really sure why this is a bad idea. Seems much cleaner than the other approaches I have seen above.Hooknosed
@DanPantry I am actually seeing some deadlocks now with this approach that I don't understand. Could you expand on why it is a bad idea?Hooknosed
My bad. I got. this working now. My problem was that I was creating the task on the main thread and then passed that task to the invoke async method. Thanks @Mule your code helped me out.Hooknosed
H
-4

I have found that SpinWait works pretty well for this.

var task = Task.Run(()=>DoSomethingAsyncronous());

if(!SpinWait.SpinUntil(()=>task.IsComplete, TimeSpan.FromSeconds(30)))
{//Task didn't complete within 30 seconds, fail...
   return false;
}

return true;

The above approach doesn't need to use .Result or .Wait(). It also lets you specify a timeout so that you're not stuck forever in case the task never completes.

Hibben answered 6/2, 2019 at 22:2 Comment(2)
This is polling (spinning), the delegate will be taking thread from pool up to 1000 times per second. It may not return control immediately after task is finished (up to 10+ms error). If finished by timeout the task will continues running, which makes timeout practically useless.Pisces
Actually, I'm using this all over the place in my code and when the condition is met, SpinWaitSpinUntil() immediately exits. So whichever comes first, 'condition met' or timeout, the task exits. It does not continue to run.Hibben
A
-5
    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;
        }
    }
Aun answered 27/9, 2013 at 20:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.