In Asp.Net MVC project I have an Asynchronus controller, wand I also have an action of type Task to implement Asynchronus functionality.
Well I have the following code,
Action startMethod = delegate() { CreateUserFunctionality(user.UserID); };
return Task.Factory.StartNew(startMethod).ContinueWith(
t =>
{
return (ActionResult)RedirectToAction("Profile", "UserProfile");
});
And I also has, CreateUserFunctionality as
public void CreateUserFunctionality(int UsrId)
{
if (UsrId !=0)
{
_userService.NewFunctionality(UsrId);
}
}
I expect that the function will start the job, CreateUserFunctionality and returns the view for me which is written in ContinueWith function. However in my case that continuewith part always waits for the CreateUserFunctionality to complete. My aim was to return the view immediatly and not wait for CreateUserFunctionality to finish, as It should continue in background. Please let me find out what I am missing here.
Ofcourse it will wait because you are using ContinueWith method.If you don't want to wait just start your task, and leave it.Return your ActionResult immediately:
var task = Task.Factory.StartNew(startMethod);
return RedirectToAction("Profile", "UserProfile");
From documentation:
Task.ContinueWith Method : Creates a continuation that executes
asynchronously when the target Task completes.
So it is working as expected.
Related
In my .Net 6 WebPI service, I am queueing work to a background task queue, very closely based on the example here, but I could post parts of my implementation if that would help:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks
I am running into unexpected behavior where control is not returned to the caller, even after the return Ok(..) completes in the controller. Instead the request only completes after the await Task.Delay(1000); line is reached on the queued work item. The request returns to the client as soon as this line is reached, and does not need to wait for the Delay to finish.
I'm guessing this is because of the await either starting a new async context, or someone un-sticking the async context of the original request. My intention is for the request to complete immediately after queuing the work item.
Any insight into what is happening here would be greatly appreciated.
Controller:
public async Task<ActionResult> Test()
{
var resultMessage = await _testQueue.DoIt();
return Ok(new { Message = resultMessage });
}
Queueing Work:
public TestAdapter(ITaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
public async Task<string> DoIt()
{
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) =>
{
await Task.Delay(1000);
var y = 12;
});
return "cool";
}
IoC:
services.AddHostedService<QueueHostedService>();
services.AddSingleton<ITTaskQueue>(ctx =>
{
return new TaskQueue(MaxQueueCount);
});
TaskQueue:
private readonly Channel<BackgroundTaskContext> _queue;
public TaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<BackgroundTaskContext>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(new BackgroundTaskContext(workItem, ...));
}
Not sure what you expect here. I'm assuming you want the async method to return the cool in the api response. That's fine but because your also awaiting the async call with in DoIt(), then it pauses until QueueBackgroundWorkItemAsync finishes. You could remove the await and it will run and return as you expect.
I can't say that I'm a big fan of that design as you lose contact with it with exception of the cancellation token. Another thought would be to Send that work off to a console job or function app using message bus or even another http call.
Additional Notes:
Async can be complicated to explain because in reality, it wraps up code and executes on a thread of it's choosing. The await simulates the synchronous behavior.
await Task.Delay(1000); // Runs on it's own thread but still halts code execution for 1 second.
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) // Waits for control to be returned from the code inside.
var resultMessage = await _testQueue.DoIt(); // Always waits for the code inside to complete.
If your wanting something to run without pausing code execution, you can either remove the await or add a Task.Run(() => { }); pattern. Is it a good idea is a whole other question. It also matters whether you need information back from the async method. If you don't await it then you'll get null back as it doesn't wait around for the answer to be computed.
This appears just to have been user error using the debugger. The debugger is switching to the background task thread and hitting breakpoints there before the response fully returns giving the appearance that control was not being returned to the client and being carried into the background task.
Even after adding some synchronous steps in QueueBackgroundWorkItemAsync and putting breakpoints on them, control was not immediately returned to the original http call. Only after I tried adding a long running task like await Task.Delay(1000); did enough time/ticks pass for the http response to return. I had conflated this with just the await somehow freeing up the original http context.
Supposed I have to get login information from the server then login, then after the login do something next. The goal is to encapsulate all of this into something that caller can wait.
However each of Firebase C# API call already returned separate tasks (that has already been started). My approach is to think of how to chain each of these separate task together.
I am thinking of making a new Task that represent this since it also seems great for aggregating all the exceptions for the caller to catch.
We have
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
GetValueAsync as I understand already spun up the task. We can observe the returned task or better use continuation to begin something when it finished.
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(username, password);
Auth.SignInWithEmailAndPasswordAsync also spun up a new task in itself. I can also use continuation to wait and begin something else.
After the login I would like to use another database call to get user data.
So
public Task Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
Task loginAfterGetKeyTask = getKeyTask.ContinueWith(gkt =>
{
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(gkt.Result.___.username, gkt.Result.____.password);
loginTask.ContinueWith(lt => { ....the next task.... });
});
return loginAfterGetKeyTask;
}
There are 2 problems I found :
As it goes on the code is getting deeper in the pyramid. Is there any way I can do to make the code looks more sequential?
This Login() function I want to return a Task so that caller can wait for the whole login process to finish then continue, etc. But currently the returned loginAfterGetKey task finished even if there are more things to do inside. I could keep doing .ContinueWith chaining on the outside if not for that there are one more Firebase task that is executed. Ideally I would like to return the Task that represent the whole thing but they are nested inside. Firebase's method start those task on it's own so I think I could not try to bundle all the task on my own.
I tried encapsulate all the tasks as a new Task start from Task.Factory.StartNew however "GetComponent can only be called from main thread" is a problem. (The Auth and other Firebase service would require GetComponent)
I tried using IEnumerator as a return of Login() and planned to put yield in this function, however, "The yield statement cannot be used inside anonymous method or lambda expression".
I tried using loginTask.Wait() inside the first ContinueWith in order to make loginAfterGetKeyTask that I return from this function not arrive at IsCompleted state until it reacehs the end (which would have to wait for the tasks inside) but since the lambda in this ContinueWith is in the main thread, it causes deadlock.
In the end I used IEnumerator loop waiting as a kind of hack to wait for the Task to finish. It 'linearize' the Task multi-thread system into single thread coroutine system that Unity seems to prefer. Each yield return null results in resuming in the next frame to check on the Task's status again.
Not very elegant but I could not find any better way outside async/await that pairs with Task but Unity's C# version does not yet support it.
public void Caller()
{
yield return StartCoroutine(Login());
// Login finished, check the login credentials
}
private IEnumerator WaitTask(Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
public IEnumerator Login()
{
Task<DataSnapshot> getKeyTask = Database.Child("keyLocation").GetValueAsync();
yield return WaitTask(getKeyTask);
Task loginTask = Auth.SignInWithEmailAndPasswordAsync(getKeyTask.Result.___.username, getKeyTask.Result.____.password);
yield return WaitTask(loginTask);
//... the next task can use loginTask.Result etc.
}
It would be tricky still to catch exception since compiler does not allow try catch to wrap the yield. One way I might use the callback pattern to send the exception to the caller.
Here's an extension version in case you want to do like yield return task.YieldWait(); instead.
public static class TaskExtension
{
/// <summary>
/// Firebase Task might not play well with Unity's Coroutine workflow. You can now yield on the task with this.
/// </summary>
public static IEnumerator YieldWait(this Task task)
{
while (task.IsCompleted == false)
{
yield return null;
}
if(task.IsFaulted)
{
throw task.Exception;
}
}
}
I have an async method that fetches some data from a database. This operation is fairly expensive, and takes a long time to complete. As a result, I'd like to cache the method's return value. However, it's possible that the async method will be called multiple times before its initial execution has a chance to return and save its result to the cache, resulting in multiple calls to this expensive operation.
To avoid this, I'm currently reusing a Task, like so:
public class DataAccess
{
private Task<MyData> _getDataTask;
public async Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return await _getDataTask;
}
}
My thought is that the initial call to GetDataAsync will kick off the synchronousDataAccessMethod method in a Task, and any subsequent calls to this method before the Task has completed will simply await the already running Task, automatically avoiding calling synchronousDataAccessMethod more than once. Calls made to GetDataAsync after the private Task has completed will cause the Task to be awaited, which will immediately return the data from its initial execution.
This seems to be working, but I'm having some strange performance issues that I suspect may be tied to this approach. Specifically, awaiting _getDataTask after it has completed takes several seconds (and locks the UI thread), even though the synchronousDataAccessMethod call is not called.
Am I misusing async/await? Is there a hidden gotcha that I'm not seeing? Is there a better way to accomplish the desired behavior?
EDIT
Here's how I call this method:
var result = (await myDataAccessObject.GetDataAsync()).ToList();
Maybe it has something to do with the fact that the result is not immediately enumerated?
If you want to await it further up the call stack, I think you want this:
public class DataAccess
{
private Task<MyData> _getDataTask;
private readonly object lockObj = new Object();
public async Task<MyData> GetDataAsync()
{
lock(lockObj)
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
}
return await _getDataTask;
}
}
Your original code has the potential for this happening:
Thread 1 sees that _getDataTask == null, and begins constructing the task
Thread 2 sees that _getDataTask == null, and begins constructing the task
Thread 1 finishes constructing the task, which starts, and Thread 1 waits on that task
Thread 2 finishes constructing a task, which starts, and Thread 2 waits on that task
You end up with two instances of the task running.
Use the lock function to prevent multiple calls to the database query section. Lock will make it thread safe so that once it has been cached all the other calls will use it instead of running to the database for fulfillment.
lock(StaticObject) // Create a static object so there is only one value defined for this routine
{
if(_getDataTask == null)
{
// Get data code here
}
return _getDataTask
}
Please rewrite your function as:
public Task<MyData> GetDataAsync()
{
if (_getDataTask == null)
{
_getDataTask = Task.Run(() => synchronousDataAccessMethod());
}
return _getDataTask;
}
This should not change at all the things that can be done with this function - you can still await on the returned task!
Please tell me if that changes anything.
Bit late to answer this but there is an open source library called LazyCache that will do this for you in two lines of code and it was recently updated to handle caching Tasks for just this sort of situation. It is also available on nuget.
Example:
Func<Task<List<MyData>>> cacheableAsyncFunc = () => myDataAccessObject.GetDataAsync();
var cachedData = await cache.GetOrAddAsync("myDataAccessObject.GetData", cacheableAsyncFunc);
return cachedData;
// Or instead just do it all in one line if you prefer
// return await cache.GetOrAddAsync("myDataAccessObject.GetData", myDataAccessObject.GetDataAsync);
}
It has built in locking by default so the cacheable method will only execute once per cache miss, and it uses a lamda so you can do "get or add" in one go. It defaults to 20 minutes sliding expiration but you can set whatever caching policy you like on it.
More info on caching tasks is in the api docs and you may find the sample app to demo caching tasks useful.
(Disclaimer: I am the author of LazyCache)
I'm hoping there is a simple answer here, and this is probably due to my misunderstanding of asynchronous operations...
I have a method that can be started manually or can autostart when the program loads. The async method works perfectly when invoked manually (on button press). However, when autoloaded the method just seems to skip the main "await" part of the method without performing any work, and skips straight to the end.
The whole process starts in this method:
private void StartBatch()
{
var batchSize = (int)BatchSizeSlider.Value;
if (_config.AutoStart)
{
ExecutionLogAddItem(string.Format("Auto batch processing started (batch size: {0})", batchSize.ToString()));
Task.Factory.StartNew(async () =>
{
await BatchTransfer(batchSize);
CompleteBatch();
});
}
else
{
var start = ConfirmStartBatch();
var doBatch = start.ContinueWith(async (continuation) =>
{
//start task
if (start.Result == true)
{
ExecutionLogAddItem("Batch processing started.");
ExecutionLogAddItem(string.Format("Batch size set at {0}", batchSize.ToString()));
await BatchTransfer(batchSize).ContinueWith((x) => CompleteBatch());
}
else
{
ExecutionLogAddItem("Batch processing aborted.");
}
});
}
}
If _config.AutoStart is true, the BatchTransfer method doesn't seem to do anything, instead the program skips straight to the CompleteBatch() method. If invoked manually everything works as expected.
The strange thing is, if I set a breakpoint on await BatchTransfer(batchSize) in the autostarted method, I can step through the code and the batch transfers take place. So when debugging it works, when not debugging it doesn't. Please help!
It is because -
Task.Factory.StartNew(async () =>
{
await BatchTransfer(batchSize);
CompleteBatch();
});
You are waiting for the inner task to complete with await but Task.Factory.StartNew(async () => itself is an asynchronous task and is not awaited. You should also wait for Task.Factory.StartNew(async () => like this -
await Task.Factory.StartNew(async () =>
When you are debugging, the separate thread that is calling inner task is held and you can see the execution but when running normally the background is still working, but you cannot see it since you didn't wait for the Task.Factory.StartNew(async () =>.
If you check the thread pool and thread id, I am sure you will see that they are different when debugging.
This blog might help you understand the situation - http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
In order to use await you have to make your method async and call it without Task.Factory.StartNew. One more thing, instead of void make return type as Task of your method because void async are fire and forget. You will not able to track them.
private async Task StartBatch()
{
await BatchTransfer(batchSize);
CompleteBatch();
}
Check this link. It have very basic demonstration of async and it is very helpful in understanding how asynchrony works. Six Essential Tips For Async - Introduction. It includes six tips which are very essential. I recommend you go through all of them but to understand current question situation you can go through Tip 1 whose title is Async void is for top-level event-handlers only.
I have a Partial Action that needs to call an async method that returns Task from a DB Call. Since I can't have an async Partial Action I need to wait for the task to complete:
public ActionResult TopMenu()
{
var topMenuTask = Task.Run<IEnumerable<TopMenuItem>>( () =>
{ return _menuService.GetTopMenu(); });
topMenuTask.Wait();
//view model population code goes here
return PartialView(viewModel);
}
It is my understanding the benefit of doing an async DB (IO) call is we are requeueing the thread to the thread pool so it can serve more requests while the IO is finishing, but wouldn't this be a moo point in this case as we are synchronously waiting for it?
I assume from your problem description that GetTopMenu returns a Task, and thus should actually be called GetTopMenuAsync.
In this case, your best option is probably what you are already doing:
public ActionResult TopMenu()
{
var topMenuTask = Task.Run(() => _menuService.GetTopMenuAsync());
var topMenu = topMenuTask.Result;
//view model population code goes here
return PartialView(viewModel);
}
It is my understanding the benefit of doing an async DB (IO) call is we are requeueing the thread to the thread pool so it can serve more requests while the IO is finishing, but wouldn't this be a moo point in this case as we are synchronously waiting for it?
Correct. By wrapping the (asynchronous) DB access within a (synchronous) partial action, the code is nullifying the benefits of async.
This is a limitation of the platform (ASP.NET MVC). Please vote on the issue and on uservoice.
You will summon a thread from the thread pool that will execute that logic but the web request thread will be blocked waiting for the result. So no, there is no benefit.
Why canĀ“t you do this?:
public async Task<ActionResult> TopMenu()
{
var topMenu = await Task.Run<IEnumerable<TopMenuItem>>( () => { return _menuService.GetTopMenu(); });
//view model population code goes here
return PartialView(viewModel);
}
Another option is to use the classic async MVC way, but you have to derive your controller from AsyncController rather than Controller:
public void TopMenuAsync()
{
AsyncManager.OutstandingOperations.Increment();
Task.Run<IEnumerable<TopMenuItem>>(() => { return _menuService.GetTopMenu(); })
.ContinueWith(t =>
{
AsyncManager.Parameters["topMenu"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult TopMenuCompleted(TopMenuItem topMenu)
{
//view model population code goes here
return PartialView(viewModel);
}
You should not be using Task.Run in ASP.NET in the fist place. You're still limited by a single HTTP request/response time frame, it won't speed up the content delivery and will just introduce an extra thread switch, check this.
So, your action should look like this:
public ActionResult TopMenu()
{
var topMenu = return _menuService.GetTopMenu();
//view model population code goes here
return PartialView(viewModel);
}
If your partial views may take a while to render, consider using a technique like this:
http://blog.michaelckennedy.net/2012/11/13/improve-perceived-performance-of-asp-net-mvc-websites-with-async-partialviews/
You are correct.. and it is generally the reason why you defer the decision of using a Task up to the caller, or provide async and non-async versions of the call (e.g: GetTopMenu and GetTopMenuAsync). If you force the use of an async call then you run into situations just like this.
Your alternative is to load the view and asynchronously make the call via AJAX. You can also give the user visual feedback in this scenario. Utilising await in this scenario will free up the worker process for IIS to continue serving requests.