Avoid async from spreading in method signatures - c#

What prevents me from constantly using the async/wait pattern in my .net code is that once you create an async method the async keyword tends to spread thru my code forcing me to make all methods async. Is there a pattern to stop this efficiently?

What prevents me from constantly using the async/wait pattern in my .net code is that once you create an async method the async keyword tends to spread thru my code forcing me to make all methods async. Is there a pattern to stop this efficiently?
Let me ask you this, then: why are you using async/await? You need to take a step back and decide if you want the benefits of asynchronous code. If you do want those benefits, then your code must be asynchronous. As soon as you block a thread, you lose all the benefits of async. So really, why use asynchronous code at all, if you're just going to block a thread anyway?
That said, there are some scenarios where a partially-async stack makes sense. For example, if your code base is temporarily in a transition state. In this case, you can use one of the hacks described in my article on brownfield async:
Blocking directly (may cause deadlocks).
Blocking on a thread pool thread (executes code on a different thread and different context, may cause unexpected parallelism).
Blocking on the current thread with a thread pool context (executes code in a different context, may cause unexpected parallelism).
Blocking on a thread with a single-threaded context (executes code on a different thread and different context).
Blocking on a nested message loop (may cause unexpected reentrancy).
All of these are hacks, and all have different drawbacks. There is no hack that works in every scenario.

As commented earlier you should go async all the way...
In some cases we use a litle helper class to cut off the async pattern. This class alows you to wait for the result without ending up with a deadlock by replacing the SynchronizationContext.
public class NoSynchronizationContextScope : IDisposable
{
private readonly SynchronizationContext synchronizationContext;
public NoSynchronizationContextScope()
{
synchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(null);
}
public void Dispose() => SynchronizationContext.SetSynchronizationContext(synchronizationContext);
}
void fn()
{
using (new NoSynchronizationContextScope())
{
fnAsync().Wait();
// or
// var result = fnAsync().Result();
// in case you have to wait for a result
}
}

In the very particular case of a method that you want to start running but not wait for the result of (i.e. triggering an event handler) you can use async void. Just make sure you catch all the errors at that point:
public async void DoSomething() {
try {
var o = await DoThing1();
await DoThing2(o);
} catch (Exception e) {
// Log the error somewhere (to a file, database, etc.)
}
}

You can use async method and invoke not async method in task so outer method will behave like async and inner method is still sync.
public async Task OuterFuncAsync()
{
await Task.Run(() => InnerFunc());
}

Related

C# Convert async code to run sync in certain method(Constructor/Main/PropertyGetter/PropertySetter) without possibility of deadlock

I have a bunch of async code, I have tried to expand my asyc code as large as possible in my codebase. I am here looking for an safe approach to convert async code to async (wait it finish then return result). I have tied looking on the internet but they either way to complex or may cause deadlocks.
Here is my code:
protected virtual DbData GetDbData()
{
return StorageProvider.RefreshAsync().Result;
}
The GetDbData will be used as a property getter something like:
public override DbData Data
{
get => GetDbData();
set => SetDbData(value);
}
and in constructor
public CachedDataManager(IStorageProvider storageProvider) : base(storageProvider)
{
_cachedData = StorageProvider.RefreshAsync().Result;
}
or for the async method without return value
public CachedDataManager(IStorageProvider storageProvider) : base(storageProvider)
{
DoSomeWorkAsync().GetAwaiter().GetResult();
}
private Task DoSomeWorkAsync()
{
//Assume heavy load.
Task.Delay(5000);
return Task.CompletedTask;
}
Result may causing a deadlock because of the SynchronizationContext when calling in the UI thead (what I know)
All the other solution told me to expand async code as far as possible, I tried, but I can't do it with constructor/property getter
Are there exist some solution similar to Task.Result/Task.Wait/Task.RunSynchronously that does not causes any problems(eg deadlock)?
Also I am not sure about DoSomeWorkAsync().GetAwaiter().GetResult(); it does causes problems with SynchronizationContext or not. I am a noob on this. Please help
A task (what async methods return) can contain every type of logic. Suppose you are calling an async Task method from the Thread A, and you want to synchronously wait for the task to complete. Now let's think carefully what could be the conditions for a deadlock: basically we have to guarantee that the Task do not schedule anything "asynchronously" on the Thread A! If that's the case you can safely call task.GetAwaiter().GetResult(). If not you can't. It's a conceptual limit. There is not a general way to avoid deadlocks.
For example suppose that the Thread A is the UI thread. If the async Task contains a "real" await operation (that is, an await over a Task that is not completed yet, such as Task.Delay(100)), we are already in a bad spot: why? Because the code after the await is wrapped into an Action and scheduled through the Synchronization Context in the UI Thread. So the task is waiting for some code to execute in the UI thread, but the UI thread is waiting for the task to complete
Another example: suppouse the Task method we are awaiting is not async (so contains synchronous code, like Task.Run(VoidMethod)) and that at some point there is a call to Dispatcher.Invoke(). We still have a deadlock: the task to complete should wait for some code to be executed on the UI Thread. But the UI Thread is waiting for that task to complete..
Side note. While in the first example we can do some tricks such as change the Synchronization context temporarily in order to avoid the continuations to run on the UI Thread, in the second example there are no chance to make it work, it's conceptually wrong if you thing about it. So there isn't a general way.
It really depends.

Difference between calling an async method and Task.Run an async method

I have a method in my view model
private async void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
this.SyncContacts();
}
}
private async Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
CloudInstance.AddContacts(contactsToUpload);
}
When I call SyncData from the UI commands and I'm syncing a large chunk of data UI freezes. But when I call SyncContacts with this approach
private void SyncData(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
Task.Run(() => this.SyncContacts());
}
}
Everything is fine. Should not they be the same?
I was thinking that not using await for calling an async method creates a new thread.
Should not they be the same? I was thinking that not using await for
calling an async method creates a new thread.
No, async does not magically allocate a new thread for it's method invocation. async-await is mainly about taking advantage of naturally asynchronous APIs, such as a network call to a database or a remote web-service.
When you use Task.Run, you explicitly use a thread-pool thread to execute your delegate. If you mark a method with the async keyword, but don't await anything internally, it will execute synchronously.
I'm not sure what your SyncContacts() method actually does (since you haven't provided it's implementation), but marking it async by itself will gain you nothing.
Edit:
Now that you've added the implementation, i see two things:
I'm not sure how CPU intensive is your synchronous data analysis, but it may be enough for the UI to get unresponsive.
You're not awaiting your asynchronous operation. It needs to look like this:
private async Task SyncDataAsync(SyncMessage syncMessage)
{
if (syncMessage.State == SyncState.SyncContacts)
{
await this.SyncContactsAsync();
}
}
private Task SyncContactsAsync()
{
foreach(var contact in this.AllContacts)
{
// do synchronous data analysis
}
// ...
// AddContacts is an async method
return CloudInstance.AddContactsAsync(contactsToUpload);
}
What your line Task.Run(() => this.SyncContacts()); really does is creating a new task starting it and returning it to the caller (which is not used for any further purposes in your case). That's the reason why it will do its work in the background and the UI will keep working. If you need to (a)wait for the task to complete, you could use await Task.Run(() => this.SyncContacts());. If you just want to ensure that SyncContacts has finished when you return your SyncData method, you could using the returning task and awaiting it at the end of your SyncData method. As it has been suggested in the comments: If you're not interested in whether the task has finished or not you just can return it.
However, Microsoft recommend to don't mix blocking code and async code and that async methods end with Async (https://msdn.microsoft.com/en-us/magazine/jj991977.aspx). Therefore, you should consider renaming your methods and don't mark methods with async, when you don't use the await keyword.
Just to clarify why the UI freezes - the work done in the tight foreach loop is likely CPU-bound and will block the original caller's thread until the loop completes.
So, irrespective of whether the Task returned from SyncContacts is awaited or not, the CPU bound work prior to calling AddContactsAsync will still occur synchronously on, and block, the caller's thread.
private Task SyncContacts()
{
foreach(var contact in this.AllContacts)
{
// ** CPU intensive work here.
}
// Will return immediately with a Task which will complete asynchronously
return CloudInstance.AddContactsAsync(contactsToUpload);
}
(Re : No why async / return await on SyncContacts- see Yuval's point - making the method async and awaiting the result would have been wasteful in this instance)
For a WPF project, it should be OK to use Task.Run to do the CPU bound work off the calling thread (but not so for MVC or WebAPI Asp.Net projects).
Also, assuming the contactsToUpload mapping work is thread-safe, and that your app has full usage of the user's resources, you could also consider parallelizing the mapping to reduce overall execution time:
var contactsToUpload = this.AllContacts
.AsParallel()
.Select(contact => MapToUploadContact(contact));
// or simpler, .Select(MapToUploadContact);

What's the correct way to run multiple parallel tasks in an asp.net process?

I think I'm not understanding something. I had thought that Task.Yield() forced a new thread/context to be started for a task but upon re-reading this answer it seems that it merely forces the method to be async. It will still be on the same context.
What's the correct way - in an asp.net process - to create and run multiple tasks in parallel without causing deadlock?
In other words, suppose I have the following method:
async Task createFileFromLongRunningComputation(int input) {
//many levels of async code
}
And when a certain POST route is hit, I want to simultaneously launch the above methods 3 times, return immediately, but log when all three are done.
I think I need to put something like this into my action
public IHttpAction Post() {
Task.WhenAll(
createFileFromLongRunningComputation(1),
createFileFromLongRunningComputation(2),
createFileFromLongRunningComputation(3)
).ContinueWith((Task t) =>
logger.Log("Computation completed")
).ConfigureAwait(false);
return Ok();
}
What needs to go into createFileFromLongRunningComputation? I had thought Task.Yield was correct but it apparently is not.
The correct way to offload concurrent work to different threads is to use Task.Run as rossipedia suggested.
The best solutions for background processing in ASP.Net (where your AppDomain can be recycled/shut down automatically together with all your tasks) are in Scott Hanselman and Stephen Cleary's blogs (e.g. HangFire)
However, you could utilize Task.Yield together with ConfigureAwait(false) to achieve the same.
All Task.Yield does is return an awaiter that makes sure the rest of the method doesn't proceed synchronously (by having IsCompleted return false and OnCompleted execute the Action parameter immediately). ConfigureAwait(false) disregards the SynchronizationContext and so forces the rest of the method to execute on a ThreadPool thread.
If you use both together you can make sure an async method returns a task immediately which will execute on a ThreadPool thread (like Task.Run):
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Yield().ConfigureAwait(false);
// executed on a ThreadPool thread
}
Edit:
George Mauer pointed out that since Task.Yield returns YieldAwaitable you can't use ConfigureAwait(false) which is a method on the Task class.
You can achieve something similar by using Task.Delay with a very short timeout, so it wouldn't be synchronous but you wouldn't waste much time:
async Task CreateFileFromLongRunningComputation(int input)
{
await Task.Delay(1).ConfigureAwait(false);
// executed on a ThreadPool thread
}
A better option would be to create a YieldAwaitable that simply disregards the SynchronizationContext the same as using ConfigureAwait(false) does:
async Task CreateFileFromLongRunningComputation(int input)
{
await new NoContextYieldAwaitable();
// executed on a ThreadPool thread
}
public struct NoContextYieldAwaitable
{
public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
public struct NoContextYieldAwaiter : INotifyCompletion
{
public bool IsCompleted { get { return false; } }
public void OnCompleted(Action continuation)
{
var scheduler = TaskScheduler.Current;
if (scheduler == TaskScheduler.Default)
{
ThreadPool.QueueUserWorkItem(RunAction, continuation);
}
else
{
Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
}
}
public void GetResult() { }
private static void RunAction(object state) { ((Action)state)(); }
}
}
This isn't a recommendation, it's an answer to your Task.Yield questions.
(l3arnon's answer is the correct one. This answer is more of a discussion on whether the approach posed by the OP is a good one.)
You don't need anything special, really. The createFileFromLongRunningComputation method doesn't need anything special, just make sure you are awaiting some async method in it and the ConfigureAwait(false) should avoid the deadlock, assuming you're not doing anything out of the ordinary (probably just file I/O, given the method name).
Caveat:
This is risky. ASP.net will most likely pull the rug out from under you in this situation if the tasks take too long to finish.
As one of the commenters pointed out, there are better ways of accomplishing this. One of them is HostingEnvironment.QueueBackgroundWorkItem (which is only available in .NET 4.5.2 and up).
If the long running computation takes a significantly long time to complete, you're probably better off keeping it out of ASP.net entirely. In that situation, a better method would be to use some sort of message queue, and a service that processes those messages outside of IIS/ASP.net.

How to safely wait for async method?

I've got a method which looks like this:
static async Task<string> GetGooglePage(ProxyInfo proxy)
{
using (var m=new MyNetworkClass())
{
m.Proxy = proxy;
return await m.GetAsync("http://www.google.com");
}
}
now I want to call it from method that is not async, and get the result.
The way I was trying to do it is like this:
foreach (var proxy in proxys)
{
try
{
GetGooglePage(proxy.ToProxyInfo()).Wait();
}
catch
{}
lock (Console.Out)
{
Console.Write(current++ + "\r");
}
}
My problem is that sometimes GetGooglePage(proxy.ToProxyInfo()).Wait(); deadlocks (according to visual studio debugger, there is no stack beyond this call).
I don't want to use async all the way through to Main(). How do I correctly call GetGooglePage from synchronous code without risking a deadlock?
You're running into a deadlock that I describe on my blog.
There is no standard solution for blocking on an async method, because there is no solution that works in every situation. The only officially recommended solution is to use async all the way. Stephen Toub has a good summary of workarounds here.
One approach is to execute the async method in a background thread (via Task.Run) and then Wait on that. Disadvantages of this approach are: 1) it won't work for async methods that require a specific context (e.g., write to an ASP.NET response, or update a UI); 2) changing from a synchronized context to the unsynchronized thread pool context may introduce race conditions; and 3) it burns a thread just waiting for another thread.
Another approach is to execute the async method within a nested message loop. Disadvantages of this approach are: 1) the "nested message loop" is different for every platform (WPF vs. WinForms, etc); and 2) nested loops introduce reentrancy issues (which have been responsible for many, many bugs in the Win32 era).
The best option is not to do this: if you synchronously wait for an async method, there is no reason for the method to be async in the first place. So, what you should do (assuming you really want to or have to make the top level method synchronous) is to make all the methods synchronous.
If you can't do that, then you can prevent the deadlock by using ConfigureAwait(false):
static async Task<string> GetGooglePage(ProxyInfo proxy)
{
using (var m=new MyNetworkClass())
{
m.Proxy = proxy;
return await m.GetAsync("http://www.google.com").ConfigureAwait(false);
}
}
This way, when the method resumes, it won't try resuming on the UI thread, so you won't get the deadlock. If you're using await in GetAsync() or the methods it calls, you will need to do the same thing there too.
In general, it's a good idea to use ConfigureAwait(false) everywhere, as long as you don't need to resume of the UI thread.

Different behavior when using ContinueWith or Async-Await

When I use an async-await method (as the example below) in a HttpClient call, this code causes a deadlock. Replacing the async-await method with a t.ContinueWith, it works properly. Why?
public class MyFilter: ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var user = _authService.GetUserAsync(username).Result;
}
}
public class AuthService: IAuthService {
public async Task<User> GetUserAsync (string username) {
var jsonUsr = await _httpClientWrp.GetStringAsync(url).ConfigureAwait(false);
return await JsonConvert.DeserializeObjectAsync<User>(jsonUsr);
}
}
This works:
public class HttpClientWrapper : IHttpClient {
public Task<string> GetStringAsync(string url) {
return _client.GetStringAsync(url).ContinueWith(t => {
_log.InfoFormat("Response: {0}", url);
return t.Result;
});
}
This code will deadlock:
public class HttpClientWrapper : IHttpClient {
public async Task<string> GetStringAsync(string url) {
string result = await _client.GetStringAsync(url);
_log.InfoFormat("Response: {0}", url);
return result;
}
}
I describe this deadlock behavior on my blog and in a recent MSDN article.
await will by default schedule its continuation to run inside the current SynchronizationContext, or (if there is no SynchronizationContext) the current TaskScheduler. (Which in this case is the ASP.NET request SynchronizationContext).
The ASP.NET SynchronizationContext represents the request context, and ASP.NET only allows one thread in that context at a time.
So, when the HTTP request completes, it attempts to enter the SynchronizationContext to run InfoFormat. However, there is already a thread in the SynchronizationContext - the one blocked on Result (waiting for the async method to complete).
On the other hand, the default behavior for ContinueWith by default will schedule its continuation to the current TaskScheduler (which in this case is the thread pool TaskScheduler).
As others have noted, it's best to use await "all the way", i.e., don't block on async code. Unfortunately, that's not an option in this case since MVC does not support asynchronous action filters (as a side note, please vote for this support here).
So, your options are to use ConfigureAwait(false) or to just use synchronous methods. In this case, I recommend synchronous methods. ConfigureAwait(false) only works if the Task it's applied to has not already completed, so I recommend that once you use ConfigureAwait(false), you should use it for every await in the method after that point (and in this case, in each method in the call stack). If ConfigureAwait(false) is being used for efficiency reasons, then that's fine (because it's technically optional). In this case, ConfigureAwait(false) would be necessary for correctness reasons, so IMO it creates a maintenance burden. Synchronous methods would be clearer.
An explanation on why your await deadlocks
Your first line:
var user = _authService.GetUserAsync(username).Result;
blocks that thread and the current context while it waits for the result of GetUserAsync.
When using await it attempts to run any remaining statements back on the original context after the task being waited on finishes, which causes deadlocks if the original context is blocked (which is is because of the .Result). It looks like you attempted to preempt this problem by using .ConfigureAwait(false) in GetUserAsync, however by the time that that await is in effect it's too late because another await is encountered first. The actual execution path looks like this:
_authService.GetUserAsync(username)
_httpClientWrp.GetStringAsync(url) // not actually awaiting yet (it needs a result before it can be awaited)
await _client.GetStringAsync(url) // here's the first await that takes effect
When _client.GetStringAsync finishes, the rest of the code can't continue on the original context because that context is blocked.
Why ContinueWith behaves differently
ContinueWith doesn't try to run the other block on the original context (unless you tell it to with an additional parameter) and thus does not suffer from this problem.
This is the difference in behavior that you noticed.
A solution with async
If you still want to use async instead of ContinueWith, you can add the .ConfigureAwait(false) to the first encountered async:
string result = await _client.GetStringAsync(url).ConfigureAwait(false);
which as you most likely already know, tells await not to try to run the remaining code on the original context.
Note for the future
Whenever possible, attempt to not use blocking methods when using async/await. See Preventing a deadlock when calling an async method without using await for avoiding this in the future.
Granted, my answer is only partial, but I'll go ahead with it anyway.
Your Task.ContinueWith(...) call does not specify the scheduler, therefore TaskScheduler.Current will be used - whatever that is at the time. Your await snippet, however, will run on the captured context when the awaited task completes, so the two bits of code may or may not produce similar behaviour - depending on the value of TaskScheduler.Current.
If, say, your first snippet is called from the UI code directly (in which case TaskScheduler.Current == TaskScheduler.Default, the continuation (logging code) will execute on the default TaskScheduler - that is, on the thread pool.
In the second snippet, however, the continuation (logging) will actually run on the UI thread regardless of whether you use ConfigureAwait(false) on the task returned by GetStringAsync, or not. ConfigureAwait(false) will only affect the execution of the code after the call to GetStringAsync is awaited.
Here's something else to illustrate this:
private async void Form1_Load(object sender, EventArgs e)
{
await this.Blah().ConfigureAwait(false);
// InvalidOperationException here.
this.Text = "Oh noes, I'm no longer on the UI thread.";
}
private async Task Blah()
{
await Task.Delay(1000);
this.Text = "Hi, I'm on the UI thread.";
}
The given code sets the Text within Blah() just fine, but it throws a cross-threading exception inside the continuation in the Load handler.
I found the other solutions posted here did not work for me on ASP .NET MVC 5, which still uses synchronous Action Filters. The posted solutions don't guarantee a new thread will be used, they just specify that the same thread does not HAVE to be used.
My solution is to use Task.Factory.StartNew() and specifying TaskCreationOptions.LongRunning in the method call. This ensures a new/different thread is always used, so you can be assured you will never get a deadlock.
So, using the OP example, the following is the solution that works for me:
public class MyFilter: ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
// var user = _authService.GetUserAsync(username).Result;
// Guarantees a new/different thread will be used to make the enclosed action
// avoiding deadlocking the calling thread
var user = Task.Factory.StartNew(
() => _authService.GetUserAsync(username).Result,
TaskCreationOptions.LongRunning).Result;
}
}

Categories

Resources