How does nunit successfully wait for async void methods to complete? - c#

When using async/await in C#, the general rule is to avoid async void as that's pretty much a fire and forget, rather a Task should be used if no return value is sent from the method. Makes sense. What's strange though is that earlier in the week I was writing some unit tests for a few async methods I wrote, and noticed that NUnit suggested to mark the async tests as either void or returning Task. I then tried it, and sure enough, it worked. This seemed really strange, as how would the nunit framework be able to run the method and wait for all asynchronous operations to complete? If it returns Task, it can just await the task, and then do what it needs to do, but how can it pull it off if it returns void?
So I cracked open the source code and found it. I can reproduce it in a small sample, but I simply cannot make sense of what they're doing. I guess I don't know enough about the SynchronizationContext and how that works. Here's the code:
class Program
{
static void Main(string[] args)
{
RunVoidAsyncAndWait();
Console.WriteLine("Press any key to continue. . .");
Console.ReadKey(true);
}
private static void RunVoidAsyncAndWait()
{
var previousContext = SynchronizationContext.Current;
var currentContext = new AsyncSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(currentContext);
try
{
var myClass = new MyClass();
var method = myClass.GetType().GetMethod("AsyncMethod");
var result = method.Invoke(myClass, null);
currentContext.WaitForPendingOperationsToComplete();
}
finally
{
SynchronizationContext.SetSynchronizationContext(previousContext);
}
}
}
public class MyClass
{
public async void AsyncMethod()
{
var t = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
Console.WriteLine("Done sleeping!");
});
await t;
Console.WriteLine("Done awaiting");
}
}
public class AsyncSynchronizationContext : SynchronizationContext
{
private int _operationCount;
private readonly AsyncOperationQueue _operations = new AsyncOperationQueue();
public override void Post(SendOrPostCallback d, object state)
{
_operations.Enqueue(new AsyncOperation(d, state));
}
public override void OperationStarted()
{
Interlocked.Increment(ref _operationCount);
base.OperationStarted();
}
public override void OperationCompleted()
{
if (Interlocked.Decrement(ref _operationCount) == 0)
_operations.MarkAsComplete();
base.OperationCompleted();
}
public void WaitForPendingOperationsToComplete()
{
_operations.InvokeAll();
}
private class AsyncOperationQueue
{
private bool _run = true;
private readonly Queue _operations = Queue.Synchronized(new Queue());
private readonly AutoResetEvent _operationsAvailable = new AutoResetEvent(false);
public void Enqueue(AsyncOperation asyncOperation)
{
_operations.Enqueue(asyncOperation);
_operationsAvailable.Set();
}
public void MarkAsComplete()
{
_run = false;
_operationsAvailable.Set();
}
public void InvokeAll()
{
while (_run)
{
InvokePendingOperations();
_operationsAvailable.WaitOne();
}
InvokePendingOperations();
}
private void InvokePendingOperations()
{
while (_operations.Count > 0)
{
AsyncOperation operation = (AsyncOperation)_operations.Dequeue();
operation.Invoke();
}
}
}
private class AsyncOperation
{
private readonly SendOrPostCallback _action;
private readonly object _state;
public AsyncOperation(SendOrPostCallback action, object state)
{
_action = action;
_state = state;
}
public void Invoke()
{
_action(_state);
}
}
}
When running the above code, you'll notice that the Done Sleeping and Done awaiting messages show up before the Press any key to continue message, which means the async method is somehow being waited on.
My question is, can someone care to explain what's happening here? What exactly is the SynchronizationContext (I know it's used to post work from one thread to another) but I'm still confused as to how we can wait for all the work to be done. Thanks in advance!!

A SynchronizationContext allows posting work to a queue that is processed by another thread (or by a thread pool) -- usually the message loop of the UI framework is used for this.
The async/await feature internally uses the current synchronization context to return to the correct thread after the task you were waiting for has completed.
The AsyncSynchronizationContext class implements its own message loop. Work that is posted to this context gets added to a queue.
When your program calls WaitForPendingOperationsToComplete();, that method runs a message loop by grabbing work from the queue and executing it.
If you set a breakpoint on Console.WriteLine("Done awaiting");, you will see that it runs on the main thread within the WaitForPendingOperationsToComplete() method.
Additionally, the async/await feature calls the OperationStarted() / OperationCompleted() methods to notify the SynchronizationContext whenever an async void method starts or finishes executing.
The AsyncSynchronizationContext uses these notifications to keep a count of the number of async methods that are running and haven't completed yet. When this count reaches zero, the WaitForPendingOperationsToComplete() method stops running the message loop, and the control flow returns to the caller.
To view this process in the debugger, set breakpoints in the Post, OperationStarted and OperationCompleted methods of the synchronization context. Then step through the AsyncMethod call:
When AsyncMethod is called, .NET first calls OperationStarted()
This sets the _operationCount to 1.
Then the body of AsyncMethod starts running (and starts the background task)
At the await statement, AsyncMethod yields control as the task is not yet complete
currentContext.WaitForPendingOperationsToComplete(); gets called
No operations are available in the queue yet, so the main thread goes to sleep at _operationsAvailable.WaitOne();
On the background thread:
at some point the task finishes sleeping
Output: Done sleeping!
the delegate finishes execution and the task gets marked as complete
the Post() method gets called, enqueuing a continuation that represents the remainder of the AsyncMethod
The main thread wakes up because the queue is no longer empty
The message loop runs the continuation, thus resuming execution of AsyncMethod
Output: Done awaiting
AsyncMethod finishes execution, causing .NET to call OperationComplete()
the _operationCount is decremented to 0, which marks the message loop as complete
Control returns to the message loop
The message loop finishes because it was marked as complete, and WaitForPendingOperationsToComplete returns to the caller
Output: Press any key to continue. . .

Related

What to do when I can't await an async call

I am working on an old WinForms application. I'm trying to make a piece of code run asynchronously, so it doesn't block the UI thread. I'm having some difficulties applying the pattern. Here's a simplified version of my code (before modifications):
void InitializeMyGui()
{
//some code
if(someObject.SomeConditionIsMet())
{
//some code
}
else
{
//some other code
}
// some more code
}
This method is called from two places. One is the constructor:
public MyGui()
{
// some code
InitializeMyGui();
// some more code
}
The other is a method that is triggered by an event handler (this application is older than async/await, event driven programming was used to achieve asynchronous execution)
private void OnSomeOtherStuffFinishedLoading(object sender, EventArgs e)
{
// some code
InitializeMyGui();
// some more code
}
Problem is SomeConditionIsMet() contains a database call, so it needs to be asynchronous to avoid long operations blocking the UI thread if the DB call is slow. No worries, I've modified my methods thusly:
public async Task<bool> SomeConditionIsMet() {
//some code
await dbAdapter.ExecuteAsync();
//process and return result
}
Now InitializeMyGui() obviously needs to await SomeConditionIsMet():
async void InitializeMyGui()
{
//some code
bool conditionMet = await someObject.SomeConditionIsMet();
if(conditionMet)
{
//some code
}
else
{
//some other code
}
// some more code
}
And herein lies the problem. InitializeMyGui() is now async, and should be awaited. Indeed, everything I've read regarding async/await explicitly states you should await async calls all the way to the top. But while I could probably make OnSomeOtherStuffFinishedLoading() async and have it await InitializeMyGui(), I can't make the constructor async.
In this case, it's not such a big deal. InitializeMyGui() is a void, it's not returning any value. But if I understand it correctly, if I don't await the method, the execution will continue before it returns, and the code that comes after, (in my pseudocode marked // some more code) could - and in all likelihood will - execute before InitializeMyGui() finishes.
What I'm missing is a way to say "Hey, run this async method, and then continue with this code here, but do it all asynchronously so that you're not blocking the UI thread" but without making the calling mehtod itself async. Is there a way to do this?
EDIT:
I came up with this:
Task.Run(InitializeMyGui()).ContinueWith(t => {// some other code});
Will this work, or am I somehow violating the sanctity of the UI thread?
First off, InitializeMyGui should be async Task, not async void.
To solve your problem, you need to accept that you now have an additional state in your application. Previously, the code would block the UI thread until it was ready to show the complete UI (after the DB call). What you're doing by making it asynchronous is freeing up that UI thread. But then the UI has to show something. So what you really have is a new "loading" kind of state in your app.
So, the best solution is to have your constructor (synchronously) initialize to the loading state, and then have it update to the loaded state. I wrote an article about this technique - it uses MVVM/WPF, but the concepts are the same.
The natural way to do this is to use async/await rather than ContinueWith:
public MyGui()
{
// some code
Loaded = ContinueConstructionAsync();
async Task ContinueConstructionAsync()
{
await InitializeMyGui();
// some more code
}
}
public Task Loaded { get; }
This code also adds a new property that can be awaited if other parts of the code depend on this type being loaded.
Make the constructor private, and use a static factory method to call it and your initialization code. A static method can of course be async.
class MyGui
{
private MyGui()
{
}
private async Task InitializeMyGui()
{
}
static public async Task<MyGui> CreateInstance()
{
var instance = new MyGui();
await instance.InitializeMyGui();
return instance;
}
}
Now you can instantiate an instance using async code:
var myInstance = MyGui.CreateInstance();

Can I create a C# async method without using a different thread (Task)?

I have searched a lot and it seems C# async await has to be used together with Task.
The situation is that I have a method that is very time consuming which is OK, but I hope it won't block the main method.
So I describe the main method as "async", and inside it, I call and "await" the time consuming method, but C# need the time consuming method to be included in a Task which means it will be executed in a seperate thread. But that method has something that cannot run outside main thread.
And my question is how can I run the time consuming method asynchronously without putting it in a different thread?
Thank you very much.
PS: I'm doing this in Unity3D, is it possible to leverage Coroutine to reach the goal?
//main method
private async void MainMethod()
{
//...
bool result = await TimeConsumingMethod();
//...
}
//time consuming method
private async Task<bool> TimeConsumingMethod()
{
bool result;
await Task.Run(()=>
{
//...
//SOME CODE THAT CANNOT run in thread other than main
//...
});
return result;
}
To run anything aysnchronously (not blocking the main thread) in unity you have to use a different Thread/Task.
But as you also want to run code on the main thread in that separate thread you'll have to communicate between the two.
Although you can't run anything on the main thread from inside a different thread. You can make a queue that the main thread consistently handles. When the off thread then wants to do something on the main thread it adds it to the queue and the mainThreadHandler will then handle it in the next frame update. So the off thread can then tell the main thread that it wants to do something on the main thread and wait for the main thread to be done with that code and then continue with the processing after.
This is an implementation of a main thread handler:
public class MainThreadHandler:MonoBehaviour
{
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
public void Update()
{
lock (_executionQueue)
{
while (_executionQueue.Count > 0)
{
_executionQueue.Dequeue().Invoke();
}
}
}
public static void Enqueue(Action action)
{
lock (_executionQueue)
{
_executionQueue.Enqueue(action);
}
}
}
Calling your code including calling things in the main thread and then waiting for it will then look something like this:
private Task currentTask;
private bool taskResult;
public void StartOffThreadMethod()
{
currentTask = Task.Run(() =>
{
DoCalculationsOffThread();
bool isMainThreadDone = false;
MainThreadHandler.Enqueue(() =>
{
//Main thread code
//Either set a bool that the off thread checks to see if the action has been completed
//or start a new task that handles the rest of the off threaded code
//This example uses a isDone bool
isMainThreadDone = true;
});
while (!isMainThreadDone)
{
Thread.Sleep(100);
}
DoOtherCalculationsOffThread();
taskResult = true;
});
}
private void Update()
{
if (currentTask != null && currentTask.IsCompleted)
{
//do stuff with the result
}
}
I'd also like to add that going back and forth between the main thread and an calculating thread can be rather tricky business. If it can be prevented i would try to prevent it.

Async, await in C#

On closing of my application I have to do some clean up activity and I have written something like this in ClassA.cs
protected override void OnExit(ExitEventArgs e)
{
Cleanup();
base.OnExit(e);
}
private async Task Cleanup()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
}
I have one more class ClassB.cs
public class DatabaseInitialisation : IDatabaseInitialisation
{
private readonly string filterOptimisationPath;
private readonly Task databaseInitialisation;
private IDbConnectionFactory ConnectionFactory { get; }
public async Task InitialiseDatabase() => await databaseInitialisation;
public DatabaseInitialisation(string databaseFilePath, string filterOptimisationPath)
{
this.filterOptimisationPath = filterOptimisationPath;
ConnectionFactory = new SqLiteConnectionFactory(databaseFilePath);
databaseInitialisation = Task.Run(() => CreateDatabase(databaseFilePath));
}
}
so now when I close my application, sometimes when cleanup is called execution of the program going from await is going to onexit method without executing the dispose method
The root of your trouble is that you need to call an async method from a sync method, and you cannot make that method async because it's part of the GUI framework. The general advice here would be to make your calling method async, but since you can't, there are a few ways you can accomplish this:
Option 1 -- Task.Run
Change your OnExit method to launch Cleanup on a thread pool thread and then synchronously wait for that using GetAwaiter().GetResult() to prevent deadlocks. Since this is only running once at shutdown, it should be fine, but launching background threads like this would not be recommended if this were a method that will run often as it could potentially lead to thread pool starvation.
protected override void OnExit(ExitEventArgs e)
{
Task.Run(async () => await Cleanup().ConfigureAwait(false))
.GetAwaiter().GetResult();
base.OnExit(e);
}
Option 2 -- async void and a ManualResetEvent
This approach avoids using .GetAwaiter().GetResult() and launching on a thread pool thread, but any exception thrown in cleanup will cause the application to crash.
protected override void OnExit(ExitEventArgs e)
{
using var signal = new ManualResetEventSlim(false);
Cleanup(signal);
signal.Wait();
base.OnExit(e);
}
private async void Cleanup(ManualResetEventSlim signal)
{
if (databaseInitialisation != null)
{
await databaseInitialisation.InitialiseDatabase();
}
databaseFile?.Dispose();
signal.Set();
}
You could also use one of the Wait overloads to include a timeout.
As a side note, in a GUI application like this, you should really be calling .ConfigureAWait(false) on every task you await that isn't being called directly from a GUI event. Not doing so risks deadlocks.
One caveat to both these solutions is that if your Cleanup method or anything in its call stack, both of these methods will deadlock, and in that case, another solution is needed.

How to invoke a method on specific running task

I'm new in the themes tasks.
How to get access to the right task and invoke the method on this task? The task has an ID and I gave a Name to it and I got a TaskScheduler.
I get the Exception, which is correct:
InvalidOperationException: The calling thread can not access this object because the object is owned by another thread.
Problem:
In the Main I start a Task with CustomSplashScreenViewModel.StartSplashScreenAsync(). More Tasks get startet and are running. With the Event when everything has loaded I need to Close my SplashScreen.
The method CustomSplashScreenViewModel.CloseSplash(_splashTask, _taskSchedulerSplash); get those informations. When I debug it, the _splashTask is "null" and in the _taskSchedulerSplash it has the _splashTask inside.
Inside the CloseSplash Method I like to invoke the method _view.Close() on the _splashTask.
How do I solve this?
public class Program
{
private static readonly SingleThreadTaskScheduler _taskSchedulerSplash = new SingleThreadTaskScheduler(ApartmentState.STA);
[ThreadStatic]
private static Task _splashTask;
// [STAThread]
public static async Task Main()
{
_splashTask = await Task.Factory.StartNew(() =>
{
return CustomSplashScreenViewModel.StartSplashScreenAsync();
}, _taskSchedulerSplash);
var taskScheduler = new SingleThreadTaskScheduler(ApartmentState.STA);
var taskList = new List<Task>();
var updateTask = Task.Run(InstallHelper.CheckForUpdatesAsync);
updateTask.Wait();
taskList.Add(updateTask);
var tasks = await Task.Factory.ContinueWhenAll(taskList.ToArray(), result => Task.Factory.StartNew( ()=>AppStart(container), taskScheduler));
tasks.Wait();
}
private static void App_MainWindowLoaded(object sender, EventArgs e)
{
CustomSplashScreenViewModel.CloseSplash(_splashTask, _taskSchedulerSplash);
}
}
public class CustomSplashScreenViewModel
{
private static Thread _currentThread;
public static void CloseSplash(Task task, TaskScheduler taskScheduler)
{
if (_view!= null)
{
// Here i need the right Task and to invoke the following method
// if I'm doing _view.Dispatcher.Invoke(new Action(_view.Close)); it doesn't
// work coz it's another STA Thread. Inside this class I can store the right task in
// a local variable. this works. But how to invoke it if the Task is already running?
// I like to overgive the task - may it isn't possible.
_view.Close();
}
}
}
Thanks a lot for helping me.
You can't change a running task. However, you can schedule another task that will run on the same scheduler, which should work just fine for what you need (it'll run on the same STA thread that owns the splash screen):
public static void CloseSplash(Task task, TaskScheduler taskScheduler)
{
if (_view!= null)
{
Task.Factory.StartNew(() => _view.Close(), taskScheduler);
}
}
On a side note, I'd recommend these to make the code easier to understand:
Introducing a TaskFactory instance for your custom task scheduler instead of using TaskScheduler with Task.Factory.
Avoid Task.Factory.ContinueWhenAll and Task.Wait; use Task.WhenAll and await instead.

How to catch/observe an unhandled exception thrown from a Task

I'm trying to log / report all unhandled exceptions in my app (error reporting solution). I've come across a scenario that is always unhandled. I'm wondering how would I catch this error in an unhandled manner. Please note that I've done a ton of research this morning and tried a lot of things.. Yes, I've seen this, this and many more. I'm just looking for a generic solution to log unhandled exceptions.
I have the following code inside of a console test apps main method:
Task.Factory.StartNew(TryExecute);
or
Task.Run((Action)TryExecute);
as well as the following method:
private static void TryExecute() {
throw new Exception("I'm never caught");
}
I'm already tried wiring up to the following in my app, but they are never called.
AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException
In my Wpf app where I initially found this error I also wired up to these events but it was never called.
Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException
The only handler that is called ever is:
AppDomain.CurrentDomain.FirstChanceException
but this is not a valid solution as I only want to report uncaught exceptions (not every exception as FirstChanceException is called before any catch blocks are ever executed / resolved.
The TaskScheduler.UnobservedTaskException event should give you what you want, as you stated above. What makes you think that it is not getting fired?
Exceptions are caught by the task and then re-thrown, but not immediately, in specific situations. Exceptions from tasks are re-thrown in several ways (off the top of my head, there are probably more).
When you try and access the result (Task.Result)
Calling Wait(), Task.WaitOne(), Task.WaitAll() or another related Wait method on the task.
When you try to dispose the Task without explicitly looking at or handling the exception
If you do any of the above, the exception will be rethrown on whatever thread that code is running on, and the event will not be called since you will be observing the exception. If you don't have the code inside of a try {} catch {}, you will fire the AppDomain.CurrentDomain.UnhandledException, which sounds like what might be happening.
The other way the exception is re-thrown would be:
When you do none of the above so that the task still views the exception as unobserved and the Task is getting finalized. It is thrown as a last ditch effort to let you know there was an exception that you didn't see.
If this is the case and since the finalizer is non-deterministic, are you waiting for a GC to happen so that those tasks with unobserved exceptions are put in the finalizer queue, and then waiting again for them to be finalized?
EDIT: This article talks a little bit about this. And this article talks about why the event exists, which might give you insight into how it can be used properly.
I used the LimitedTaskScheduler from MSDN to catch all exceptions, included from other threads using the TPL:
public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
{
/// Whether the current thread is processing work items.
[ThreadStatic]
private static bool currentThreadIsProcessingItems;
/// The list of tasks to be executed.
private readonly LinkedList tasks = new LinkedList(); // protected by lock(tasks)
private readonly ILogger logger;
/// The maximum concurrency level allowed by this scheduler.
private readonly int maxDegreeOfParallelism;
/// Whether the scheduler is currently processing work items.
private int delegatesQueuedOrRunning; // protected by lock(tasks)
public LimitedConcurrencyLevelTaskScheduler(ILogger logger) : this(logger, Environment.ProcessorCount)
{
}
public LimitedConcurrencyLevelTaskScheduler(ILogger logger, int maxDegreeOfParallelism)
{
this.logger = logger;
if (maxDegreeOfParallelism Gets the maximum concurrency level supported by this scheduler.
public override sealed int MaximumConcurrencyLevel
{
get { return maxDegreeOfParallelism; }
}
/// Queues a task to the scheduler.
/// The task to be queued.
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (tasks)
{
tasks.AddLast(task);
if (delegatesQueuedOrRunning >= maxDegreeOfParallelism)
{
return;
}
++delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
/// Attempts to execute the specified task on the current thread.
/// The task to be executed.
///
/// Whether the task could be executed on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!currentThreadIsProcessingItems)
{
return false;
}
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
{
TryDequeue(task);
}
// Try to run the task.
return TryExecuteTask(task);
}
/// Attempts to remove a previously scheduled task from the scheduler.
/// The task to be removed.
/// Whether the task could be found and removed.
protected sealed override bool TryDequeue(Task task)
{
lock (tasks)
{
return tasks.Remove(task);
}
}
/// Gets an enumerable of the tasks currently scheduled on this scheduler.
/// An enumerable of the tasks currently scheduled.
protected sealed override IEnumerable GetScheduledTasks()
{
var lockTaken = false;
try
{
Monitor.TryEnter(tasks, ref lockTaken);
if (lockTaken)
{
return tasks.ToArray();
}
else
{
throw new NotSupportedException();
}
}
finally
{
if (lockTaken)
{
Monitor.Exit(tasks);
}
}
}
protected virtual void OnTaskFault(AggregateException exception)
{
logger.Error(exception);
}
///
/// Informs the ThreadPool that there's work to be executed for this scheduler.
///
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(ExcuteTask, null);
}
private void ExcuteTask(object state)
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (tasks.Count == 0)
{
--delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = tasks.First.Value;
tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
TryExecuteTask(item);
if (!item.IsFaulted)
{
continue;
}
OnTaskFault(item.Exception);
}
}
finally
{
// We're done processing items on the current thread
currentThreadIsProcessingItems = false;
}
}
}
And than the "registration" of the TaskScheduler as the default using Reflection:
public static class TaskLogging
{
private const BindingFlags StaticBinding = BindingFlags.Static | BindingFlags.NonPublic;
public static void SetScheduler(TaskScheduler taskScheduler)
{
var field = typeof(TaskScheduler).GetField("s_defaultTaskScheduler", StaticBinding);
field.SetValue(null, taskScheduler);
SetOnTaskFactory(new TaskFactory(taskScheduler));
}
private static void SetOnTaskFactory(TaskFactory taskFactory)
{
var field = typeof(Task).GetField("s_factory", StaticBinding);
field.SetValue(null, taskFactory);
}
}

Categories

Resources