This question already has an answer here:
eliding async and await in async methods [duplicate]
(1 answer)
Closed 4 years ago.
When passing through method calls to another async method, should the caller method also be async and use await, or should I simply pass through the Task it receives from the callee? What if the calling method performs a bit more preparation?
public Task<Message> Unsubscribe(int subscriptionId, CancellationToken cancellationToken)
{
var data = new MessageData
{
["subscriptionId"] = subscriptionId
};
return SendAsync(OpCode.Unsubscribe, data, cancellationToken);
}
public Task<Message> Unsubscribe(int subscriptionId) =>
Unsubscribe(subscriptionId, CancellationToken.None);
SendAsync is async and returns Task<Message>. So should the first overload of Unsubscribe be like above or like that:
public async Task<Message> Unsubscribe(int subscriptionId, CancellationToken cancellationToken)
{
var data = new MessageData
{
["subscriptionId"] = subscriptionId
};
return await SendAsync(OpCode.Unsubscribe, data, cancellationToken);
}
The other alternative is with the second overload of Unsubscribe. It might be like above or like that:
public async Task<Message> Unsubscribe(int subscriptionId) =>
await Unsubscribe(subscriptionId, CancellationToken.None);
I guess that more asyncs and awaits add complexity introduced by the compiler (I see it in the stack traces!) and may degrade performance and memory consumption. But at least it should provide for a consistent exception propagation.
In the examples you cited, just returning the task without awaiting it is fine (and arguably preferable), but this does require some care.
Once case were you can get into trouble is when you're dealing with Tasks inside a using block. These can have vastly different behaviors:
public async Task<Something> AwaitTheTask()
{
using (var someResource = GetAResource())
{
return await SomeAsyncThing(someResource);
}
}
public Task<Something> DontAwaitTheTask()
{
using (var someResource = GetAResource())
{
return SomeAsyncThing(someResource);
}
}
In the first example, the using block will not dispose someResource until the awaited Task has completed. In the second example, someResource will be disposed right away, very likely causing problems for the code that needs that resource.
Related
I'm trying to call an async task (SIn) within a synch method (SignIn). I need the synch method because I'm passing ref to that method. But when I'm calling the async task, the GUI is frozen. The async task is a simple login with the onedrive sdk.
I've tried to waited the task, but the GUI still frozen. I've also tried creating a new Thread, but it didn't work too. How can I call the async method?
public override bool SignIn(ref User user)
{
try
{
signInEnd = false;
signinUser = user;
Task<bool> task = SIn();
task.Wait();
return task.Result;
}
catch(Exception e)
{
return false;
}
}
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync();
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
Calling Wait() blocks the UI thread which means that the continuation of SIn(), i.e. the part that will eventually be executed once the Task returned by AuthenticateUserAsync() has completed, won't be able to execute on this thread. This results in a deadlock.
You may be able to get around this by avoiding capturing the context by calling ConfigureAwait(false) in SIn():
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync().ConfigureAwait(false);
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
But the "real" solution to this kind of issues is not to mix asynchronous and synchronous code, i.e. SignIn should be asynchronous and await SIn(). Don't block on asynchronous code by calling Wait() or Result:
public Task<bool> SignIn(User user)
{
try
{
...
return await SIn();
}
catch (Exception e)
{
return false;
}
}
Please refer to #Stephen Cleary's blog post for more information about this.
mm8 is right that not calling async from inside a sync method is the best way to solve your issue,
remember that the public async void EventHandler() method was specifically designed for running long running tasks from a gui linked control
However it isn't always possible to rewrite an entire system to be async when only one small section needs changing
In this case you should avoid waiting for the results as this makes the async process pointless, what you can do though is break your synchronous code into 2 parts a before and after
the before method will prep and launch the task,
the after handles the results
ie
public async Task<string> GetData(int delay)
{
await Task.Delay(delay);
return "complete";
}
public void StartGettingData()
{
GetData(5000).ContinueWith(t => CompleteGetData(t.Result), TaskScheduler.Current);
}
public void CompleteGetData(string message)
{
UpdateStatus(message);
}
this method does have the added complexity of requiring you to ensure thread safety yourself, which is why the async/await functionality was introduced
I have the following Hub class in SignalR part and here I defined all of the methods related to connection:
public override Task OnConnected()
{
// here I cannot call this, and need to convert this method async
await AddToGroup("stockGroup");
//
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
return base.OnConnected();
}
public async Task AddToGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} joined");
}
I have a look at many different examples regarding to these connection methods (and other Hub methods) and see that some of them use async method while some of them not. In this example above, I need to convert OnConnected() method to async in order to call AddToGroup() method. Of course the reverse situation would also be ok, but I am not sure which one is better. So, should I use async methods or non-async methods for all of the methods in the Hub? Any help would be appreciated.
Update 1 : Converted method (to async).
public override async Task OnConnected()
{
await AddToGroup("stockGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
return base.OnConnected();
}
Update 2:
public override async Task OnConnected()
{
// #1 There is no async method in "Microsoft.AspNet.SignalR" library.
//await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
// #2 I just have sync version of "AddToGroupAsync()" and used it
await Groups.Add(Context.ConnectionId, "SignalR Users");
/* #3 I think there is no need to use this custom method in the Hub.
Because the same method is already exist in the IGroupManager interface */
//await AddToGroup("jiraGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
// #4 Here also the same problem and I used sync version of OnConnected()
//await base.OnConnectedAsync();
await base.OnConnected();
}
Just add async if you need await inside method. async is a marker for compiler to generate async state machine.
should I use async- use if you need to await.
If you don't need to await just return task itself without async. In that case you can avoid creation of state machine.
Also please read Async/Await FAQ from Stephen Toub:
When you mark a method with the “async” keyword, you’re really telling
the compiler two things:
You’re telling the compiler that you want to be able to use the
“await” keyword inside the method (you can use the await keyword if
and only if the method or lambda it’s in is marked as async). In doing
so, you’re telling the compiler to compile the method using a state
machine, such that the method will be able to suspend and then resume
asynchronously at await points.
You’re telling the compiler to “lift”
the result of the method or any exceptions that may occur into the
return type. For a method that returns Task or Task, this
means that any returned value or exception that goes unhandled within
the method is stored into the result task. For a method that returns
void, this means that any exceptions are propagated to the caller’s
context via whatever “SynchronizationContext” was current at the time
of the method’s initial invocation.
You should use Async methods with await and return Task.
Asynchrony is viral so you should avoid async void.
Bad:
public async void MethodAsync()
{
var result = await SendAsync();
DoSomething(result);
}
Good:
public async Task MethodAsync()
{
var result = await SendAsync();
DoSomething(result);
}
There are some awesome async guidelines by #davidfowl here.
Update: remove the return:
public override async Task OnConnected()
{
await AddToGroup("stockGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
await base.OnConnected();
}
I am working on a DelegatingHandler to intercept all the calls from one service to another. And I am totally new to DelegatingHandlers.
This is the issue.
We currently have an API that takes calls and measure the concurrent calls and it has some mechanisms to throttle it if needed. it is in this form. (Since actual API is heavy for debugging I am actually using following stub methods to test this)
private Task<HttpResponseMessage> ExecuteAsync1(Func<Task<HttpResponseMessage>> action)
{
return ExecuteAsync2(action);
}
private async Task<HttpResponseMessage> ExecuteAsync2(Func<Task<HttpResponseMessage>> action)
{
return await action().ConfigureAwait(false);
}
Now within this Delegating handler I have to use this API like this.
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (this.ThrottlingFactory == null || !this.ThrottlingEnabled)
{
return await base.SendAsync(request, cancellationToken);
}
else
{
var response = await this.ExecuteAsync1(() =>
{
return base.SendAsync(request, cancellationToken);
});
// Analyse response for certain thing here.
return response;
}
}
My problem is, after executing ExecuteAsync1 (it actually completes the ExecuteAsync2) the code never return back. I tried numerous variation of this like adding async/await to all the methods in the chain including the anon method etc. non worked. So can somebody please point me to things I am doing wrong here?
This is what I had to do. I had to ConfigureAwait(false) to the call.
var response = await this.ExecuteAsync1(() =>
{
return base.SendAsync(request, cancellationToken);
}).ConfigureAwait(false);
I seem to be getting a TaskCanceledException whenever I return another Task synchronously instead of awaiting it, following the guidelines in When at last you await.
TaskCanceledException code
public static class Download
{
public static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return FromYouTubeAsync(
() => client
.GetStringAsync(videoUri),
uri => client
.GetByteArrayAsync(uri));
}
}
public async static Task<byte[]> FromYouTubeAsync(
Func<Task<string>> sourceFactory, Func<string, Task<byte[]>> downloadFactory)
{
string source = await // TaskCanceledException here
sourceFactory()
.ConfigureAwait(false);
// find links
return await
downloadFactory(links.First())
.ConfigureAwait(false);
}
}
Exception-free code
Here, the first overload of the method's signature is changed to async, and it awaits the second overload. For some reason, this prevents the TaskCanceledException.
public static class Download
{
public async static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return await FromYouTubeAsync(
() => client
.GetStringAsync(videoUri),
uri => client
.GetByteArrayAsync(uri));
}
}
public async static Task<byte[]> FromYouTubeAsync(
Func<Task<string>> sourceFactory, Func<string, Task<byte[]>> downloadFactory)
{
string source = await // No exception!
sourceFactory()
.ConfigureAwait(false);
// find links
return await
downloadFactory(links.First())
.ConfigureAwait(false);
}
}
Why does this happen and what can I do to fix it (besides awaiting the method, which wastes resources as described in the link above)?
Sorry, the link you posted is about applying an optimization which is only applicable if the method does nothing after its await. To quote the post:
In this case, however, we’re being handed a task to represent the last statement in the method, and thus it’s in effect already a representation of the entire method’s processing...
In your example, the task does not represent the last statement in the method. Look again:
public async static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return await FromYouTubeAsync(...);
}
}
There's something happening after the await: specifically, the disposing of client. So the optimization mentioned in that blog post does not apply here.
This is why you're seeing an exception if you try to return the task directly:
public static Task<byte[]> FromYouTubeAsync(string videoUri)
{
using (var client = new HttpClient())
{
return FromYouTubeAsync(...);
}
}
This code is starting the download, then disposing the HttpClient, and then returning the task. HttpClient will cancel any outstanding operations when it is disposed.
The code using await will (asynchronously) wait for the HTTP operation to complete before it disposes the HttpClient. That is the behavior you need, and await is the cleanest way to express it. In this case, it's not a "waste of resources" at all, because you have to defer disposing until after the download completes.
If I have an async method body like so -
public async Task GetSomething() {
await SendText("hi");
await SendImage("bla.bmp");
}
How can I get the Task object before it is returned to the user when the await kicks in
ie..
public async Task GetSomething() {
myTasks.Add(Task.Current);
await SendText("hi");
await SendImage("bla.bmp");
//some processing
}
so that somewhere else I can do
await Task.WhenAll(myTasks);
Console.WriteLine("All tasks complete");
This is so that I can wait for all tasks to complete before shutting down
This is not directly possible as the language does not have a facility to access the "current" task.
There is a workaround though: Wrap your async method in another method. This other method can get hold of the task once the async method returns (which happens approximately at the first await point).
In all cases I recommend letting the caller add the async Task to your list, not the async method itself. This is useful even from an encapsulation point of view.