Does Task.Result have any impact when used after the await statement? - c#

I'm trying to work on a proof of concept app, and I would like to know the internals of below two Async Methods.
Would Task.Result cause any issues in DoSomethingAsync1()?
I've read a bit about some blocking and deadlock issues that Task.Result can cause, but I think it wouldn't in this case, since it runs on a separate task and there is an await on the task which already ensures the Result.
The main goal here is to run the async method on a separate task, since this operation doesn't depend on main asp.net thread, and more importantly catch any exceptions thrown by DoSomethingThatTakesReallyLong()
Also,in the ActionResult DoSomething(), should I set .ConfigureAwait(false);?
Are there any hidden bottlenecks/issues that I need to be aware of in the below scenario?
Update
I've fixed the typo I made when typing the question here. (returning user object instead of a task)
Also, I'm not at the liberty of converting all the higher level methods to async at one go in the actual application. So, this is something I'm planning to do in a step-by-step way, starting with operations that doesn't depend on main thread.
I really appreciate all the best-practices answers as well, but this being a little playground code, my main question was to know if there are internal differences between DoSomethingAsync1() and DoSomethingAsync2() that might cause any issues under a certain condition.
From reading through the comments and answers, I get an idea that there isn't much difference.
private static async Task<User> DoSomethingAsync1()
{
try
{
var longRunningTask = Task<User>.Factory.StartNew(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong());
var user = await longRunningTask;
//Will the below line of code cause any issue?
Console.WriteLine(longRunningTask.Result.Id);
return user;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
private static async Task<User> DoSomethingAsync2()
{
try
{
var longRunningTask = Task<User>.Factory.StartNew(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong());
var user = await longRunningTask;
Console.WriteLine(user.Id);
return user;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return null;
}
}
public ActionResult Index()
{
//Things that run on main Asp.NET thread
//Things that run independently
//Won't be using the task returned here at the moment.
DoSomethingAsync1();
//Do more things on main Asp.NET thread if needed
return View();
}
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Thanks

Would Task.Result cause any issues [if the task is already completed]?
Task<T>.Result will synchronously wait for the task to complete (the same as task.Wait()), and then return the result (the same as await task).
Since your task is already completed, there is only one other difference between Result and await that is important: Result will wrap exceptions in an AggregateException. For this reason (and also to make the code more refactor-safe), I use await instead of Result whenever possible. That is, take the DoSomethingAsync2 approach.
I've read a bit about some blocking and deadlock issues that Task.Result can cause, but I think it wouldn't in this case, since it runs on a separate task and there is an await on the task which already ensures the Result.
It wouldn't because it's running in the thread pool context.
So, this is something I'm planning to do in a step-by-step way, starting with operations that doesn't depend on main thread.
You may find my brownfield async article helpful. In that article, I refer to this technique as "The Thread Pool Hack".
Are there any hidden bottlenecks/issues that I need to be aware of in the below scenario?
A few:
You shouldn't use StartNew. Use Task.Run instead.
You shouldn't fire-and-forget on ASP.NET (i.e., call an asynchronous method without consuming its task).
You shouldn't expose fake-asynchronous methods (i.e., methods with an asynchronous signature but that are implemented by blocking a thread pool thread). Instead of using Task.Run to implement a method, use Task.Run to call a method.
Combining those three points, the proper structure of The Thread Pool Hack is to use Task.Run with GetAwaiter().GetResult() (which is just like Result but avoids the AggregateException wrapper):
public ActionResult Index()
{
var r = Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong())
.GetAwaiter().GetResult();
return View();
}
As a final issue, as long as you have this pattern in your ASP.NET application, the scalability of your application will be decreased. Instead of using one thread during DoSomethingThatTakesReallyLong, your app is now using two; this may also throw off the ASP.NET thread pool heuristics. This may be acceptable during the transition to async, but keep it in mind as a motivator to finish the transition.

A normal usage would be something like this:
private static async Task<User> DoSomethingAsync3()
{
try
{
var user = await Task.Run(() => LongRunner.LongRunnerInstance.DoSomethingThatTakesReallyLong());
Console.WriteLine(user.Id);
return user;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
Also, if you were to correctly follow the async all the way pattern, you should change your Index to this:
public async Task<ActionResult> Index()
{
await DoSomethingAsync3();
return View();
}

Related

KeyVaultClient hangs at GetSecretAsync [duplicate]

I have a multi-tier .Net 4.5 application calling a method using C#'s new async and await keywords that just hangs and I can't see why.
At the bottom I have an async method that extents our database utility OurDBConn (basically a wrapper for the underlying DBConnection and DBCommand objects):
public static async Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
T result = await Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
return result;
}
Then I have a mid level async method that calls this to get some slow running totals:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var result = await this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
return result;
}
Finally I have a UI method (an MVC action) that runs synchronously:
Task<ResultClass> asyncTask = midLevelClass.GetTotalAsync(...);
// do other stuff that takes a few seconds
ResultClass slowTotal = asyncTask.Result;
The problem is that it hangs on that last line forever. It does the same thing if I call asyncTask.Wait(). If I run the slow SQL method directly it takes about 4 seconds.
The behaviour I'm expecting is that when it gets to asyncTask.Result, if it's not finished it should wait until it is, and once it is it should return the result.
If I step through with a debugger the SQL statement completes and the lambda function finishes, but the return result; line of GetTotalAsync is never reached.
Any idea what I'm doing wrong?
Any suggestions to where I need to investigate in order to fix this?
Could this be a deadlock somewhere, and if so is there any direct way to find it?
Yep, that's a deadlock all right. And a common mistake with the TPL, so don't feel bad.
When you write await foo, the runtime, by default, schedules the continuation of the function on the same SynchronizationContext that the method started on. In English, let's say you called your ExecuteAsync from the UI thread. Your query runs on the threadpool thread (because you called Task.Run), but you then await the result. This means that the runtime will schedule your "return result;" line to run back on the UI thread, rather than scheduling it back to the threadpool.
So how does this deadlock? Imagine you just have this code:
var task = dataSource.ExecuteAsync(_ => 42);
var result = task.Result;
So the first line kicks off the asynchronous work. The second line then blocks the UI thread. So when the runtime wants to run the "return result" line back on the UI thread, it can't do that until the Result completes. But of course, the Result can't be given until the return happens. Deadlock.
This illustrates a key rule of using the TPL: when you use .Result on a UI thread (or some other fancy sync context), you must be careful to ensure that nothing that Task is dependent upon is scheduled to the UI thread. Or else evilness happens.
So what do you do? Option #1 is use await everywhere, but as you said that's already not an option. Second option which is available for you is to simply stop using await. You can rewrite your two functions to:
public static Task<T> ExecuteAsync<T>(this OurDBConn dataSource, Func<OurDBConn, T> function)
{
string connectionString = dataSource.ConnectionString;
// Start the SQL and pass back to the caller until finished
return Task.Run(
() =>
{
// Copy the SQL connection so that we don't get two commands running at the same time on the same open connection
using (var ds = new OurDBConn(connectionString))
{
return function(ds);
}
});
}
public static Task<ResultClass> GetTotalAsync( ... )
{
return this.DBConnection.ExecuteAsync<ResultClass>(
ds => ds.Execute("select slow running data into result"));
}
What's the difference? There's now no awaiting anywhere, so nothing being implicitly scheduled to the UI thread. For simple methods like these that have a single return, there's no point in doing an "var result = await...; return result" pattern; just remove the async modifier and pass the task object around directly. It's less overhead, if nothing else.
Option #3 is to specify that you don't want your awaits to schedule back to the UI thread, but just schedule to the thread pool. You do this with the ConfigureAwait method, like so:
public static async Task<ResultClass> GetTotalAsync( ... )
{
var resultTask = this.DBConnection.ExecuteAsync<ResultClass>(
ds => return ds.Execute("select slow running data into result");
return await resultTask.ConfigureAwait(false);
}
Awaiting a task normally would schedule to the UI thread if you're on it; awaiting the result of ContinueAwait will ignore whatever context you are on, and always schedule to the threadpool. The downside of this is you have to sprinkle this everywhere in all functions your .Result depends on, because any missed .ConfigureAwait might be the cause of another deadlock.
This is the classic mixed-async deadlock scenario, as I describe on my blog. Jason described it well: by default, a "context" is saved at every await and used to continue the async method. This "context" is the current SynchronizationContext unless it it null, in which case it is the current TaskScheduler. When the async method attempts to continue, it first re-enters the captured "context" (in this case, an ASP.NET SynchronizationContext). The ASP.NET SynchronizationContext only permits one thread in the context at a time, and there is already a thread in the context - the thread blocked on Task.Result.
There are two guidelines that will avoid this deadlock:
Use async all the way down. You mention that you "can't" do this, but I'm not sure why not. ASP.NET MVC on .NET 4.5 can certainly support async actions, and it's not a difficult change to make.
Use ConfigureAwait(continueOnCapturedContext: false) as much as possible. This overrides the default behavior of resuming on the captured context.
I was in the same deadlock situation but in my case calling an async method from a sync method, what works for me was:
private static SiteMetadataCacheItem GetCachedItem()
{
TenantService TS = new TenantService(); // my service datacontext
var CachedItem = Task.Run(async ()=>
await TS.GetTenantDataAsync(TenantIdValue)
).Result; // dont deadlock anymore
}
is this a good approach, any idea?
Just to add to the accepted answer (not enough rep to comment), I had this issue arise when blocking using task.Result, event though every await below it had ConfigureAwait(false), as in this example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
The issue actually lay with the external library code. The async library method tried to continue in the calling sync context, no matter how I configured the await, leading to deadlock.
Thus, the answer was to roll my own version of the external library code ExternalLibraryStringAsync, so that it would have the desired continuation properties.
wrong answer for historical purposes
After much pain and anguish, I found the solution buried in this blog post (Ctrl-f for 'deadlock'). It revolves around using task.ContinueWith, instead of the bare task.Result.
Previously deadlocking example:
public Foo GetFooSynchronous()
{
var foo = new Foo();
foo.Info = GetInfoAsync.Result; // often deadlocks in ASP.NET
return foo;
}
private async Task<string> GetInfoAsync()
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
Avoid the deadlock like this:
public Foo GetFooSynchronous
{
var foo = new Foo();
GetInfoAsync() // ContinueWith doesn't run until the task is complete
.ContinueWith(task => foo.Info = task.Result);
return foo;
}
private async Task<string> GetInfoAsync
{
return await ExternalLibraryStringAsync().ConfigureAwait(false);
}
quick answer :
change this line
ResultClass slowTotal = asyncTask.Result;
to
ResultClass slowTotal = await asyncTask;
why? you should not use .result to get the result of tasks inside most applications except console applications if you do so your program will hang when it gets there
you can also try the below code if you want to use .Result
ResultClass slowTotal = Task.Run(async ()=>await asyncTask).Result;

Task.Wait() on async method works locally, but not via another method

I recently resolved a problem I had while trying to execute an async method. To me, this posed more questions than it solved. In short, I am happy that it works, but I don't know why it does.
I was attempting to run the following method of the Microsoft Graph Client library:
_graphServiceClient.Users[userPrincipalName].Request().GetAsync()
The following approach did not work, as it hangs after using await:
async Task<User> GetUser(string userPrincipalName)
{
User user = await _graphServiceClient.Users[userPrincipalName].Request().GetAsync();
return user;
}
The following approach also did not work, as it hangs after executing runTask.Wait():
User GetUser(string userPrincipalName)
{
return (User) GetResult(_graphServiceClient.Users[userPrincipalName].Request().GetAsync());
}
object GetResult<TResult>(Task<TResult> task)
{
using (task)
using (var runTask = Task.Run(async () => await task))
{
try
{
runTask.Wait();
return runTask.Result;
}
catch (AggregateException e)
{
throw e.InnerException ?? e;
}
}
}
This is where things get weird, as the following code did work:
User GetUser(string userPrincipalName)
{
using (var task = Task.Run(async () => await _graphServiceClient.Users[userPrincipalName].Request().GetAsync()))
{
try
{
task.Wait();
return task.Result;
}
catch (AggregateException e)
{
throw e.InnerException ?? e;
}
}
}
While I realize that the last two approaches are not considered best practice, I am completely puzzled as to why the third approach works, while the second one does not. To me, it looks like these are nearly identical.
So why would the third method work, but not the second one?
First, you need to understand how the deadlock happens. await will (by default) capture a "context" and will resume executing the rest of the async method in that context. In the ASP.NET Classic case, this "context" is a request context, which only allows one thread to run at a time. When running on a thread pool thread, the "context" is a thread pool context, which just queues the rest of the async method to the thread pool, where it can be run by any thread pool thread.
Blocking on asynchronous code is an antipattern. In situations where you have a single-threaded context (e.g., ASP.NET Classic), you can end up in deadlock. In your first deadlocking example, you have GetUser called in the ASP.NET Classic context, so its await will capture that context. Then the calling code (also running in the ASP.NET Classic context) will block on that task. This blocks a thread in that context, which prevents GetUser from completing, so you end up with a deadlock.
But even if you don't end up in deadlock, you still end up throwing away all the benefits of asynchronous code in the first place. Consider the "working" example, which falls into another antipattern (Task.Run on ASP.NET). In this case, Task.Run causes GetAsync to run in the thread pool context, so blocking a thread in the ASP.NET Classic context doesn't deadlock. However, this is also an antipattern.
The proper solution is to go async all the way. Specifically, your controller action should be async and use await when calling asynchronous code. Just pretend that Wait() and Result don't exist, and your code will be much happier.

ASP.NET MVC Async Action doesn't work properly

I have an issue with this simple async code. The execution goes througth the TestAsync action and goes througth the delay method but when the delay method returns nothing else happens.
it seems like blocked for some reason.
public async Task<ActionResult> TestAsync()
{
try
{
var res = await doLongOperation();
return RedirectToAction("Index");
}
catch (Exception e) { }
}
private Task<bool> doLongOperation()
{
var test = new Task<bool>(() => { /*do the long operation..*/ return true; });
return test;
}
Any suggestion?
new Task<bool>(() => true) returns a task that is not started yet. This is an old syntax that requires you to invoke the Start method on the task. If you don't invoke the Start method, the task will never complete, and thus asynchronous execution will never continue (await will asynchronously wait forever).
If you simply want to delay execution, then simply use Task.Delay like this:
private async Task<bool> delay()
{
await Task.Delay(1000);
return true;
}
In general, it is always recommended to create tasks that are already started upon creation.
If you want to create a task that runs on a thread-pool thread, then use the Task.Run method.
The method posted in your question doesn't work properly because the task is not started, there's nothing finishing so your await is waiting indefinitely. If you want to use Async it's best to use async all the way down, especially in an ASP.NET context. If you are not doing anything that is truly asynchronous, then in a web development context, just allow it to be synchronous.
I honestly believe the most good that will be done here is for you to spend time studying async/await, an excellent resource is found here: Stephen Cleary Blog on Async

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.

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