ChatGPT解决这个技术问题 Extra ChatGPT

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

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

Async method:

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

Normal usage:

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

I've tried using the following:

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

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

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

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

public static void WaitWithPumping(this Task task) 
{
        if (task == null) throw new ArgumentNullException(“task”);
        var nestedFrame = new DispatcherFrame();
        task.ContinueWith(_ => nestedFrame.Continue = false);
        Dispatcher.PushFrame(nestedFrame);
        task.Wait();
}

Here is the exception and stack trace from calling RunSynchronously:

System.InvalidOperationException Message: RunSynchronously may not be called on a task unbound to a delegate. InnerException: null Source: mscorlib StackTrace:

          at System.Threading.Tasks.Task.InternalRunSynchronously(TaskScheduler scheduler)
   at System.Threading.Tasks.Task.RunSynchronously()
   at MyApplication.CustomControls.Controls.MyCustomControl.CreateAvailablePanelList() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 638
   at MyApplication.CustomControls.Controls.MyCustomControl.get_AvailablePanels() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 233
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>b__36(DesktopPanel panel) in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 597
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at MyApplication.CustomControls.Controls.MyCustomControl.<CreateOpenPanelList>d__3b.MoveNext() in C:\Documents and Settings\...\MyApplication.CustomControls\Controls\MyCustomControl.xaml.cs:line 625
   at System.Runtime.CompilerServices.TaskAwaiter.<>c__DisplayClass7.<TrySetContinuationForAwait>b__1(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at MyApplication.App.Main() in C:\Documents and Settings\...\MyApplication\obj\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
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.
@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.
@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.
@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 asynchronously
@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.

R
Rachel

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

For some background on how this works, Stephen Toub (Mr Parallel) wrote a series of posts about this. Part 1 Part 2 Part 3
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.
@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.
@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.
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.
R
Rachel

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

.Result can produce a deadlock in certain scenario's
Result can easily cause deadlock in async code, as I describe on my blog.
@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....
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.
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
J
James Ko

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

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();
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()
I like this approach best syntactically because it does not involve lambdas.
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.
docs.microsoft.com/en-us/dotnet/api/… says about GetAwaiter(), "This method is intended for compiler user rather than use directly in code."
M
Michael L Perry

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; 

Then you call task.Wait(). The data type is simply Task.
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 or 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 You are wrong. Task.Run is different than Task.Factory.StartNew in that it automatically unwraps the result already. See this article.
Can I just write Task.Run(DoSomethingAsync) instead? This removes one level of delegates.
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.
C
Community

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.


@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.
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.
@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).
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?
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!
M
Morse

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:

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. 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:

[Top] WebRequest.GetResponse() YourCode.HelperMethod() YourCode.AnotherMethod() YourCode.EventHandlerMethod() [UI Code].Plumbing() - WPF or WinForms Code [Message Loop] - WPF or WinForms Message Loop

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

[Top] WebRequest.GetResponseAsync() YourCode.HelperMethodAsync() YourCode.AnotherMethodAsync() YourCode.EventHandlerMethodAsync() [UI Code].Plumbing() - WPF or WinForms Code [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).


+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.
+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.
C
Clement

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

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.
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
How is this supposed to be used? This deadlocks in WPF: MyAsyncMethod().RunTaskSynchronously();
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.
P
Palle Due

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.


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/…
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.
J
J. Lennon

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.


This is the only one that didn't deadlock for me.
@AndreFeijo I dont know what it is, but this is essentially Task.Run(() => ..).Wait() (with slight tweaks). Both should work.
w
wenhx

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

p
pixel

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.


But how could we use this method when the async code returns something we need?
This works for cold tasks, not tasks that has begun.
a
animuson

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)


I explained this solution, check EDIT section.
This can very easily result in deadlocks when called in asynchronous situations.
@Servy make sense. So as I get correct using Wait(timeOut) can help, right?
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.
@Servy Looks like I have to implement CancellationToken for my solution.
C
Cody Gray

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.


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...
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.
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.
@gaearon: I posted the exception details and stack trace in my original question.
@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.
D
Daniel A. White

Why not create a call like:

Service.GetCustomers();

that isn't async.


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
What if you cannot write your own functions since you are using a lib?
J
Jaider

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

M
Mahesh

use below code snip

Task.WaitAll(Task.Run(async () => await service.myAsyncMethod()));

Task.WaitAll adds nothing here. Why not just Wait?
O
Ogglas

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://stackoverflow.com/a/32429753/3850405


C
Contango

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

d
donttellya

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

It is not an true "synchronously".Y ou create two threads and wait in first results of other.
and all things aside, this is a very bad idea.
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.
@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?
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.
D
Dan Nguyen

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

C
Curtis

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.


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.
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.
J
Jhollman

I know this is an old Question, but i'll like to share my solution, may not be the best but does the job:

//Declare an Event Handler:
private event EventHandler OnThemeApply;

//..somewhere in your code..

//we 'hear' the event handler
this.OnThemeApply += (object _Sender, EventArgs _E) =>
{
  //Reaches here After the Async method had finished
  
  this.OnThemeApply = null;
};
MyAsycMethod();

private void MyAsycMethod()
{
   var t = System.Threading.Tasks.Task.Factory.StartNew(delegate
   {
      //Do something here

      Invoke((MethodInvoker)(() =>
      {
         if(this.OnThemeApply != null) this.OnThemeApply(null, null); //<- Calls the Event
      }));
   });
}

may not be the best -- So why post it? Only post new answers to old questions if they clearly improve all existing answers.
u
user2113284

On wp8:

Wrap it:

Task GetCustomersSynchronously()
{
    Task t = new Task(async () =>
    {
        myCustomers = await GetCustomers();
    }
    t.RunSynchronously();
}

Call it:

GetCustomersSynchronously();

Nope, this wont work, because the task does not await the delegate from the constructor (its a delegate and not a task..)
k
ksemenenko
    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;
        }
    }

t
thor

Or you could just go with:

customerList = Task.Run<List<Customer>>(() => { return GetCustomers(); }).Result;

For this to compile make sure you reference extension assembly:

System.Net.Http.Formatting

M
Massimiliano Kraus

Try following code it works for me:

public async void TaskSearchOnTaskList (SearchModel searchModel)
{
    try
    {
        List<EventsTasksModel> taskSearchList = await Task.Run(
            () => MakeasyncSearchRequest(searchModel),
            cancelTaskSearchToken.Token);

        if (cancelTaskSearchToken.IsCancellationRequested
                || string.IsNullOrEmpty(rid_agendaview_search_eventsbox.Text))
        {
            return;
        }

        if (taskSearchList == null || taskSearchList[0].result == Constants.ZERO)
        {
            RunOnUiThread(() => {
                textViewNoMembers.Visibility = ViewStates.Visible;                  
                taskListView.Visibility = ViewStates.Gone;
            });

            taskSearchRecureList = null;

            return;
        }
        else
        {
            taskSearchRecureList = TaskFooterServiceLayer
                                       .GetRecurringEvent(taskSearchList);

            this.SetOnAdapter(taskSearchRecureList);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("ActivityTaskFooter -> TaskSearchOnTaskList:" + ex.Message);
    }
}

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now