Nested Task - The current synchronizationcontext may not be used as a taskscheduler - c#

I've got a WPF MVVM application which has the concept of "services".
Each service method gets passed a callback as on of the parameters, it creates the Task, does the work and then calls the callback in the ViewModel in the continuation.
In one of my service methods the "work" involves synchronising databases and then loading the updated database from the DB. Another method counts the number of records in a table. Here are some simplified examples.
public void GetStudents(Action<IEnumerable<Student>> callback)
{
Task.Run(() =>
{
Sync();
return students;
}).ContinueWith(task =>
{
callback(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
public void CountStudents(Action<int> callback)
{
Task.Run(() =>
{
return students.Count();
}).ContinueWith(task =>
{
callback(task.Result);
}, TaskScheduler.FromCurrentSynchronizationContext());
}
These methods were called separately from different ViewModels and they worked as expected; however (isn't there always a however) I've added an event on synchronisation completed using the Prism EventAggregator. When sync has completed, if the Student table has been update I want to re-run the count method. The problem is because this is happening from the context of the other Task I'm getting this exception.
"The current synchronizationcontext may not be used as a taskscheduler"
I've tried joining the Task to the parent task with TaskCreationOptions.AttachedToParent but that doesn't fix the issue.
The more I think about it I think I should move the Task creation to my ViewModels, it seems the most logical and correct place for it to be, but is there something I'm missing that means I don't have to do that in this instance?

Related

Synchronously wait on task in another thread

I need to do some work on a specific thread (for all intents and purposes, we can say this is the UI thread), but the method requesting that work to be done may or may not be executing in a different thread. I am completely new to multithreaded programming, but have arrived at the conclusion that the correct approach to this is to use a TaskScheduler.
After toying around for a while with a custom implementation, I found FromCurrentSynchronizationContext. This appears to do exactly what I need and save me a lot of trouble (famous last words and all that).
My question comes down to whether I am overlooking anything that will get me into trouble, or maybe I'm overcomplicating the issue altogether. Here's what I'm doing now:
TaskScheduler
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Internals
{
internal static class MainThreadTaskScheduler
{
private static readonly object taskSchedulerLock = new();
private static readonly Thread taskSchedulerThread;
private static readonly TaskScheduler taskScheduler;
static MainThreadTaskScheduler()
{
lock (taskSchedulerLock)
{
// despite calling it the "main thread", we don't really care which thread
// this is initialized with, we just need to always use the same one for
// executing the scheduled tasks
taskSchedulerThread = Thread.CurrentThread;
if (SynchronizationContext.Current is null)
{
// in implementation this may be null, a default context works
SynchronizationContext.SetSynchronizationContext(new());
}
taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
}
public static Task Schedule(Action action)
{
lock (taskSchedulerLock)
{
if (Thread.CurrentThread == taskSchedulerThread)
{
// if we are already on the main thread, just run the delegate
action();
return Task.CompletedTask;
}
return Task.Factory.StartNew(action, CancellationToken.None,
TaskCreationOptions.None, taskScheduler);
}
}
public static Task<TResult> Schedule<TResult>(Func<TResult> func)
{
lock (taskSchedulerLock)
{
if (Thread.CurrentThread == taskSchedulerThread)
{
// if we are already on the main thread, just run the delegate
return Task.FromResult(func());
}
return Task.Factory.StartNew(func, CancellationToken.None,
TaskCreationOptions.None, taskScheduler);
}
}
}
}
Usage
// ...elsewhere...
public static bool RunTaskInMainThread()
{
// we need to synchronously return the result from the main thread regardless of
// which thread we are currently executing in
return MainThreadTaskScheduler.Schedule(() => SomeMethod()).GetAwaiter().GetResult();
}
I had attempted to make RunTaskInMainThread an async method and use await, but it kept causing my program to hang rather than yielding a result. I'm sure I was just using that incorrectly, but I don't know how to implement it here (bonus question: how can I use await here?).
Am I doing anything wrong here? Is there a better way to get the same results?
You are not in the right direction. Not because you are not smart, but because in the general area that you are trying to move there are traps all over the place.
The TaskSchedulers are not compatible with async/await. They were invented before async/await was a thing, for tasks that we now call delegate-based tasks (tasks that represent the completion of a specific delegate), in contrast with the kind of tasks that are created by async methods and are now known as promise-style tasks (tasks that represent just a promise that sometime in the future they'll complete).
The SynchronizationContext class is useless by itself. It's only useful as a base class for implementing derived classes like the WindowsFormsSynchronizationContext or Stephen Cleary's AsyncContextSynchronizationContext. It's a pity that this class was not defined as abstract, like the TaskScheduler is, to prevent programmers from trying to use it as is. Implementing a proper SynchronizationContext-derived class is not trivial.
When a thread is used for scheduling work via a scheduler, either a TaskScheduler or a SynchronizationContext, the thread is then owned by the scheduler. You can't have a thread that is shared by two schedulers, or by a scheduler and some method that wants to use that thread at any time on demand. That's why when start a message loop on the UI thread with the Application.Run method, this call is blocking. Any code that follows this call will not execute before the loops is completed (before the associated windows Form is closed). The same is true and with Stephen Cleary's AsyncContext. The AsyncContext.Run call is blocking (example).
Some more links:
ParallelExtensionsExtras Tour – #7 – Additional TaskSchedulers
ParallelExtensionsExtras source code
A bare-bone SingleThreadTaskScheduler implementation

Async method: Second operation was started on this context before a previous operation completed

I'm new to Blazor and don't have much experience working with Tasks, so hopefully I'm just making a foolish mistake. I have an async method that is called via button press, but if the method is called again within 1-2 seconds I get the following exception.
Error: System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
This button is rendered for each row in a Users table. I'm trying to delete multiple user records in quick succession, but receive the above error.
Here is the code for the button press (using AntBlazor)
<Button Type="primary" Danger OnClick="#(async() => await RemoveAsync(user))">Remove User</Button>
And here is the code for the RemoveAsync method.
private async Task RemoveAsync(User user)
{
await UserService.UpdateUserAsync(user);
}
Am I misunderstanding how async/await works? or do I need to make use of Tasks to ensure the action is complete?
Edit:
Heres the UserService.UpdateUserAsync() code
public async Task<bool> UpdateUserAsync(User user)
{
_appDBContext.Users.Update(user);
await _appDBContext.SaveChangesAsync();
return true;
}
Your code
public async Task<bool> UpdateUserAsync(User user)
{
_appDBContext.Users.Update(user);
await _appDBContext.SaveChangesAsync();
return true;
}
I assume that _appDBContext is injected in the constructor, and that UserService itself is registered as Scoped.
That means that a single _appDBContext lives for the duration of your Form, accumulating tracking data. And because of async it runs the risk of being re-entered which is your direct problem.
One solution is not to inject a DbContext but a DbContextFactory.
And then it looks like:
public async Task<bool> UpdateUserAsync(User user)
{
using var dbContext = _dbContextFactory.CreateDbContext();
dBContext.Users.Update(user);
var n = await dBContext.SaveChangesAsync();
return n == 1; // just for being accurate
}
Now the context is scoped to each method. Much lighter on memory and you can have many overlapping operations.

C# ASP.NET Core Fire and forget

I have a sync controller method
public IActionResult Action(int num)
{
//operations-1
Task.Run(() => DoWork(num));
//operations-2
int a = num + 123;
return Ok(a);
}
and DoWork method
private bool DoWork(int num)
{
//operations
return true;
}
What I'm trying to do is to run DoWork method in background when calling that endpoint from Postman, but I want to get result in Postman and then debug DoWork method (from breakpoint in DoWork method) - is it possible?
For that moment, controller action and DoWork() are executing simultaneously but when I reach
return Ok(a);
applications waits for DoWork instead of returning value. I have tried also
Task.Factory.StartNew(() => DoWork());
ThreadPool.QueueUserWorkItem(o => DoWork());
but result is the same.
I want DoWork method to return value but that value is not neccessary by controller action method - it will be used in different place, not connected with that.
Use a background queue sometimes is overkill.
There are a number of sites showing a way to do when you need to access the database context. The problem of Task.Run in a controller, is that the spawned task cannot access the same context as the controller uses as it may (probably will) get disposed before that Task accesses it.
You can get round this by ensuring that the sub task only references Dependencies it knows will stay alive,
either by using a
singleton service or better for database context, using the IServiceScopeFactory .
The crux of this is to create a seperate dependency that can handle your database context or Repository. This can be injected into your controller as normal.
public void Execute(Func<IRepository, Task> databaseWork)
{
// Fire off the task, but don't await the result
Task.Run(async () =>
{
// Exceptions must be caught
try
{
using var scope = _serviceScopeFactory.CreateScope();
var repository = scope.ServiceProvider.GetRequiredService<IRepository>();
await databaseWork(repository);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
}
Then call this from your controller such as
// Delegate the slow task another task on the threadpool
_fireForgetRepositoryHandler.Execute(async repository =>
{
// Will receive its own scoped repository on the executing task
await repository.DoLOngRunningTaskAsync();;
});
Note: This builds upon Adem Catamak's answer.
Hangfire can be used, but no actual database is required because it can work with memory storage:
services.AddHangfire(opt => opt.UseMemoryStorage());
JobStorage.Current = new MemoryStorage();
While it has some overhead, Hangfire allows managing these jobs as opposed to having stuff running async and requiring custom code for simple things like run time, unhandled exceptions, custom code for DI support.
Credit: Codidact
Tasks are high level threads that make sure you are not blocking any context.
You either want to use something like RabbitMQ or IHostedService from ASP.NET Core 2.0 to do fire and forget task that kick in after a request has completed.
If you use Db in project, you can use Hangfire It is easy to use background process manager. https://www.hangfire.io/
you can use it very easyly like BackgroundJob.Enqueue(() => DoWork(num));

ASP.NET 4.6 async controller method loses HttpContext.Current after await

I have an ASP.NET app targeting .NET 4.6 and I'm going crazy trying to figure out why HttpContext.Current becomes null after the first await inside my async MVC controller action.
I've checked and triple-checked that my project is targeting v4.6 and that the web.config's targetFramework attribute is 4.6 as well.
SynchronizationContext.Current is assigned both before and after the await and it's the right one, i.e. AspNetSynchronizationContext, not the legacy one.
FWIW, the await in question does switch threads on continuation, which is probably due to the fact that it invokes external I/O-bound code (an async database call) but that shouldn't be a problem, AFAIU.
And yet, it is! The fact that HttpContext.Current becomes null causes a number of problems for my code down the line and it doesn't make any sense to me.
I've checked the usual recommendations and I'm positive I'm doing everything I should be. I also have absolutely no ConfigureAwait's in my code!
What I DO have, is a couple of async event handlers on my HttpApplication instance:
public MvcApplication()
{
var helper = new EventHandlerTaskAsyncHelper(Application_PreRequestHandlerExecuteAsync);
AddOnPreRequestHandlerExecuteAsync(helper.BeginEventHandler, helper.EndEventHandler);
helper = new EventHandlerTaskAsyncHelper(Application_PostRequestHandlerExecuteAsync);
AddOnPostRequestHandlerExecuteAsync(helper.BeginEventHandler, helper.EndEventHandler);
}
I need these two because of custom authorization & cleanup logic, which requires async. AFAIU, this is supported and shouldn't be a problem.
What else could possibly be the reason for this puzzling behavior that I'm seeing?
UPDATE: Additional observation.
The SynchronizationContext reference stays the same after await vs. before await. But its internals change in between as can be seen in screenshots below!
BEFORE AWAIT:
AFTER AWAIT:
I'm not sure how (or even if) this might be relevant to my problem at this point. Hopefully someone else can see it!
I decided to define a watch on HttpContext.Current and started stepping "into" the await to see where exactly it changes. To no surprise, the thread was switched multiple times as I went on, which made sense to me because there were multiple true async calls on the way. They all preserved the HttpContext.Current instance as they are supposed to.
And then I hit the offending line...
var observer = new EventObserver();
using (EventMonitor.Instance.Observe(observer, ...))
{
await plan.ExecuteAsync(...);
}
var events = await observer.Task; // Doh!
The short explanation is that plan.ExecuteAsync performs a number of steps which are reported to a specialized event log in a non-blocking manner via a dedicated thread. This being business software, the pattern of reporting events is quite extensively used throughout the code. Most of the time, these events are of no direct concern to the caller. But one or two places are special in that the caller would like to know which events have occurred as a result of executing a certain code. That's when an EventObserver instance is used, as seen above.
The await observer.Task is necessary in order to wait for all relevant events to be processed and observed. The Task in question comes from a TaskCompletionSource instance, owned by the observer. Once all events have trickled in, the source's SetResult is called from a thread that processed the events. My original implementation of this detail was - very naively - as follows:
public class EventObserver : IObserver<T>
{
private readonly ObservedEvents _events = new ObservedEvents();
private readonly TaskCompletionSource<T> _source;
private readonly SynchronizationContext _capturedContext;
public EventObserver()
{
_source = new TaskCompletionSource<T>();
// Capture the current synchronization context.
_capturedContext = SynchronizationContext.Current;
}
void OnCompleted()
{
// Apply the captured synchronization context.
SynchronizationContext.SetSynchronizationContext(_capturedContext);
_source.SetResult(...);
}
}
I can now see that calling SetSynchronizationContext before SetResult isn't doing what I hoped it would be. The goal was to apply the original synchronization context to the continuation of the line await observer.Task.
The question now is: how do I do that properly? I'm guessing it will take an explicit ContinueWith call somewhere.
UPDATE
Here's what I did. I passed the TaskCreationOptions.RunContinuationsAsynchronously option the TaskCompletionSource ctor and modified the Task property on my EventObserver class to include explicitly synchronized continuation:
public Task<T> Task
{
get
{
return _source.Task.ContinueWith(t =>
{
if (_capturedContext != null)
{
SynchronizationContext.SetSynchronizationContext(_capturedContext);
}
return t.Result;
});
}
}
So now, when a code calls await observer.Task, the continuation will make sure the correct context is entered first. So far, it seems to be working correctly!

Load lists from Entity Framework without make the controller wait

I want to load a set of list at the begining of my Web App, to use them later. These lists are static and are read from the Data Base using Entity Framework as ORM. The idea is to load the list at the begining (on the home page after login) and use them all over de App. But I don't want that the home page was waiting for the list to finish loagind. I have tried several alternatives but no one works (or it goes sync or got errors).
1. First attempt: Calling ToListAsync() and Await to the ToListAsync (Throw Exception from EF)
There are two version of the list one async and other sync:
private static Task<List<EMPRESA>> ListaEmpresasAsync;
public static List<EMPRESA> ListaEmpresas
I have defined a function that generates the lists from the repository on EF
public async static Task<List<T>> GenerateAsyncListFromRepository(IGenericRepositoryBlockable<T> repository)
{
IQueryable<T> queryAsync = repository.GetAllActive();
return await queryAsync.ToListAsync();
}
And other one another function to check the result from the async calls:
public static List<T> ForceToLoadAsyncList(Task<List<T>> task)
{
task.Wait();
return task.Result;
}
The I async load the list:
ListaEmpresasAsync = TaskManager<EMPRESA>.GenerateAsynListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));
And when the List is needed I force to load:
public static List<EMPRESA> ListaEmpresas
{
get
{
return TaskManager<EMPRESA>.ForceToLoadAsyncList(ListaEmpresasAsync);
}
}
This initial approach throws the following error:
A second operation started on this context before a previous asynchronous operation completed
2. Second: Use ToList() and await to the end of the Task.Run() (Problem with empty enumerables)
Then I tested to use ToList() instead ToListAsync:
return await Task.Run(() => { return queryAsync.ToList(); });
I doesn't work either.
3. Third: Use ToList() and await the GenerationList the Force just return the List (Behaviour similar to sync approach. The controller is waiting for all the lists loading)
Following this approach I change the signature of the funcion to return list:
return queryAsync.ToList();
And await on the load process
ListaEmpresasAsync = await TaskManager<EMPRESA>.GenerateAsynListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));
But this is working similar to the sync process, that means that on the home page the loading time is really high.
I know that on EF, only one async call is allowed for each context. I just want to put all these loading process on the background even if they run sync, and when the list is needed check for the result of the task.
Any idea?
Just for clarification the solution proposed by Yuval Itzchakov should work when the DbContext is not shared among all the loading lists calls, but In my scenario it throws the error related to more than one async call on Entity Framework.
There are a couple of things wrong with your code.
First, you need to know that even when making an async call using async-await, the request doesn't complete until the awaited operation returns. This is significant, because if what you need is a Fire and Forget style operation, this wont do.
If you do want to execute asynchronously (not fire and forget), do the following:
public static List<EMPRESA> ListaEmpresas
public static Task<List<T>> GenerateAsyncListFromRepository(IGenericRepositoryBlockable<T> repository)
{
return repository.GetAllActive().ToListAsync();
}
And then call it like this:
ListaEmpresas = await TaskManager<EMPRESA>.GenerateAsyncListFromRepository((IEmpresaRepositorio)DIResolver.GetService(typeof(IEmpresaRepositorio)));
Making two lists and using Task.Wait is useless, you aren't forcing anything other then your thread waiting on the async method to return, which means it runs synchronously.
If what you want is Fire and Forget, note the following:
Using Task.Run in ASP.NET is dangerous, as it doesn't register the queued work with the ASP.NET ThreadPool, and is exposed to IIS recycling which may cause your background work to terminate unexpectedly. Instead, if you're using .NET 4.5.2 you can use HostingEnvironment.QueueBackgroundWorkItem which registers the queued task for you. If not, look into Stephan Clearys BackgroundTaskManager
You can do this parallel tasks with EF, however the intention of Using 1 DBContext in parallel threads will end in tears.
The instance members of DBContext are not thread safe ! DBContext info
You can use a simple pattern that spans threads
Thread n+1
Thread N+2
....
And await the result.
Each thread must use its own context.
I use this pattern to search many object types at once for a given search string.
But have have a wait to get the overall result is part of the design.
Not sure if that suits your requirement.
Parallel tasks demo
If you have non volatile data sets, you can get IIS to cache results.
I use this approach on a Controller Method with NON volatile content.
See the OutputCache Annotation.
[OutputCache(Duration = int.MaxValue, VaryByParam = "id", Location = OutputCacheLocation.ServerAndClient)]

Categories

Resources