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

472,349

Solution 1

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

Solution 2

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

Solution 3

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

Solution 4

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; 

Solution 5

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.

Share:
472,349
Rachel
Author by

Rachel

"The three chief virtues of a programmer are: Laziness, Impatience and Hubris." - Larry Wall Laziness: I'm too lazy to do the same task repeatedly so write scripts to do that task for me. This makes people think I am intelligent. Impatience: I'm too impatient to wait for my code to run so rewrite the code to improve performance. This makes people think I am a good programmer. Hubris: When someone asks if I can do something I just say Yes, then go find out how to do it (Google!). This makes people think I can do anything. Ultimately, it means I can make a career out of being Lazy, Impatient, and Hubristic.

Updated on July 16, 2022

Comments

  • Rachel
    Rachel almost 2 years

    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()
    
  • Rachel
    Rachel about 13 years
    That's going to be what I do if I can't get this working... create a Sync version in addition to an Async version
  • Rachel
    Rachel about 13 years
    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...
  • Rachel
    Rachel about 13 years
    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.
  • Dan Abramov
    Dan Abramov about 13 years
    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.
  • Rachel
    Rachel about 13 years
    @gaearon: I posted the exception details and stack trace in my original question.
  • Dan Abramov
    Dan Abramov about 13 years
    I'm also wondering if you're using Task from .NET 4.0 (not some preview release) and stable version of Async CTP.
  • Rachel
    Rachel about 13 years
    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.
  • Rachel
    Rachel about 13 years
    +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.
  • Cameron MacFarland
    Cameron MacFarland about 11 years
    For some background on how this works, Stephen Toub (Mr Parallel) wrote a series of posts about this. Part 1 Part 2 Part 3
  • Tom Jacques
    Tom Jacques almost 11 years
    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.
  • ghord
    ghord almost 11 years
    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?
  • Mike Marynowski
    Mike Marynowski almost 11 years
    @ghord - 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.
  • ghord
    ghord almost 11 years
    @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.
  • Mike Marynowski
    Mike Marynowski almost 11 years
    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.
  • ghord
    ghord almost 11 years
    @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.
  • class
    class almost 11 years
    If you want to use async in .NET 4.0 you can install the NuGet package async: nuget.org/packages/Microsoft.Bcl.Async
  • Rico Suter
    Rico Suter almost 11 years
    Nope, this wont work, because the task does not await the delegate from the constructor (its a delegate and not a task..)
  • Jordy Langen
    Jordy Langen over 10 years
    .Result can produce a deadlock in certain scenario's
  • AK_
    AK_ over 10 years
    @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...
  • Stephen Cleary
    Stephen Cleary over 10 years
    Result can easily cause deadlock in async code, as I describe on my blog.
  • Stephen Cleary
    Stephen Cleary over 10 years
    +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.
  • J. Lennon
    J. Lennon over 10 years
    And if the task is void (without result)?
  • AK_
    AK_ over 10 years
    @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....
  • RredCat
    RredCat over 10 years
    I explained this solution, check EDIT section.
  • Servy
    Servy over 10 years
    This can very easily result in deadlocks when called in asynchronous situations.
  • RredCat
    RredCat over 10 years
    @Servy make sense. So as I get correct using Wait(timeOut) can help, right?
  • Servy
    Servy over 10 years
    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.
  • RredCat
    RredCat over 10 years
    @Servy Looks like I have to implement CancellationToken for my solution.
  • RredCat
    RredCat over 10 years
    @Servy Why, could you explain a bit?
  • Servy
    Servy over 10 years
    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.
  • justin.lovell
    justin.lovell over 10 years
    @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.
  • Stephen Cleary
    Stephen Cleary over 10 years
    @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.
  • Cortlendt
    Cortlendt about 10 years
    task.Wait() sometimes does not work "synchronously". This introduces some very nasty bugs
  • Michael L Perry
    Michael L Perry about 10 years
    Then you call task.Wait(). The data type is simply Task.
  • sgnsajgon
    sgnsajgon over 9 years
    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.
  • sgnsajgon
    sgnsajgon over 9 years
    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.
  • sgnsajgon
    sgnsajgon over 9 years
    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.
  • sgnsajgon
    sgnsajgon over 9 years
    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.
  • sgnsajgon
    sgnsajgon over 9 years
    @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.
  • Clement
    Clement over 9 years
    That is not correct. The Task.Run will return Task<T>. See this overload msdn.microsoft.com/en-us/library/hh194918(v=vs.110).aspx
  • JonnyRaa
    JonnyRaa over 9 years
    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 hanging
  • Justin Skiles
    Justin Skiles about 9 years
    Result causes weird connection exceptions in Entity Framework
  • Mohammad Chamanpara
    Mohammad Chamanpara over 8 years
    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.
  • Ivan Danilov
    Ivan Danilov over 8 years
    @ghord sure, it still works perfectly: gist.github.com/ivan-danilov/3ee430522d596ccf6496
  • ZunTzu
    ZunTzu over 8 years
    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.
  • ZunTzu
    ZunTzu over 8 years
    @sgnsajgon You are wrong. Task.Run is different than Task.Factory.StartNew in that it automatically unwraps the result already. See this article.
  • Lee McPherson
    Lee McPherson about 8 years
    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();
  • tmt
    tmt almost 8 years
    It is not an true "synchronously".Y ou create two threads and wait in first results of other.
  • Rachel
    Rachel almost 8 years
    @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.
  • Sentinel
    Sentinel over 7 years
    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.
  • Dan
    Dan over 7 years
    and all things aside, this is a very bad idea.
  • ygoe
    ygoe over 7 years
    How is this supposed to be used? This deadlocks in WPF: MyAsyncMethod().RunTaskSynchronously();
  • Michael Kropat
    Michael Kropat over 7 years
    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.
  • mqueirozcorreia
    mqueirozcorreia over 7 years
    To avoid FxCopy warning Implement IDisposable in ExclusiveSynchronizationContext, disposing _workItemsWaiting
  • Alexei Levenkov
    Alexei Levenkov over 7 years
    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?
  • Stephen Cleary
    Stephen Cleary over 7 years
    @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.
  • Alexei Levenkov
    Alexei Levenkov over 7 years
    Ok. I'll consider bringing it up on meta than in some way.
  • AndreFeijo
    AndreFeijo about 7 years
    This is the only one that didn't deadlock for me.
  • Mark Amery
    Mark Amery almost 7 years
    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.
  • Stephen Cleary
    Stephen Cleary almost 7 years
    @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).
  • tmrog
    tmrog almost 7 years
    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.
  • tmrog
    tmrog almost 7 years
    @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?
  • tmrog
    tmrog almost 7 years
    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 @donttellya your code helped me out.
  • blackboxlogic
    blackboxlogic over 6 years
    The documentation for RunSynchronously() suggests calling Wait() afterword "to handle any exceptions that the task might throw".
  • Augusto Barreto
    Augusto Barreto over 6 years
    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()
  • ygoe
    ygoe over 6 years
    Can I just write Task.Run(DoSomethingAsync) instead? This removes one level of delegates.
  • ygoe
    ygoe over 6 years
    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?
  • dythim
    dythim over 6 years
    I like this approach best syntactically because it does not involve lambdas.
  • Michael L Perry
    Michael L Perry about 6 years
    Yep. Going the opposite direction, though, as in Task<MyResult> task = Task.Run(async () => await DoSomethingAsync()); is more explicit and addresses the concern by @sgnsajgon 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.
  • Melvyn
    Melvyn about 6 years
    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.
  • Rachel
    Rachel about 6 years
    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.
  • Eugene Y.
    Eugene Y. about 6 years
    Should be a solution for the question as it addresses the problem, minimal custom code, and answer perfectly explains key points.
  • S.Serpooshan
    S.Serpooshan over 5 years
    But how could we use this method when the async code returns something we need?
  • Marek Woźniak
    Marek Woźniak about 5 years
    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/…
  • S.Serpooshan
    S.Serpooshan about 5 years
    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!
  • S.Serpooshan
    S.Serpooshan about 5 years
    It worth to mention that based on this and this, the deadlock is not a problem for ASP.NET Core!
  • GFoley83
    GFoley83 almost 5 years
  • Sinatr
    Sinatr almost 5 years
    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.
  • Curtis
    Curtis almost 5 years
    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.
  • David Burg
    David Burg almost 5 years
    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???
  • Theophilus
    Theophilus over 4 years
    docs.microsoft.com/en-us/dotnet/api/… says about GetAwaiter(), "This method is intended for compiler user rather than use directly in code."
  • DotNet Programmer
    DotNet Programmer about 4 years
    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.
  • Zodman
    Zodman about 4 years
    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.
  • Ferenc Dajka
    Ferenc Dajka almost 4 years
    What if you cannot write your own functions since you are using a lib?
  • nawfal
    nawfal almost 4 years
    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.
  • nawfal
    nawfal almost 4 years
    @AndreFeijo I dont know what it is, but this is essentially Task.Run(() => ..).Wait() (with slight tweaks). Both should work.
  • nawfal
    nawfal almost 4 years
    Task.WaitAll adds nothing here. Why not just Wait?
  • nawfal
    nawfal almost 4 years
    This works for cold tasks, not tasks that has begun.
  • nawfal
    nawfal almost 4 years
    @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.
  • Mike Flynn
    Mike Flynn over 3 years
    I am having issues with my console application just hanging, not sure why but after using this code it started happening.
  • Mordecai
    Mordecai about 3 years
    Though this answer has been posted a very long time ago. Thank you so much, after series of searches, this worked once.
  • Rakoo
    Rakoo almost 3 years
    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); }
  • NoWar
    NoWar about 2 years
    My boy! Thank you!
  • Gert Arnold
    Gert Arnold about 2 years
    may not be the best -- So why post it? Only post new answers to old questions if they clearly improve all existing answers.