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

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();

Related

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.

One line fire and forget: void vs. async void + await

I have a void event handler that consists of exactly one line that's a call to an awaitable method.
void Handler( object sender, EventArgs args ) => AwaitableMethod();
Changing the return type to Task isn't an option, so it's fire-and-forget no matter the syntactic sugar. Even so, Visual Studio puts a green squiggly under the unawaited call, suggesting I make the handler async void and await the call.
async void Handler( object sender, EventArgs args ) => await AwaitableMethod();
My understanding is that adding async void and await in this case would just be useless overhead. Does Visual Studio know something that I don't know, or is it just being unnecessarily annoying?
My understanding is that adding async void and await in this case would just be useless overhead.
No.
If Handler returned a Task, then that would be true and eliding async/await would be fine; the code without async/await would just return the Task directly instead of "unwrapping" it with await and "wrapping" it back into a Task with async.
However, that's not the case here; Handler returns void, so the code without async/await will just ignore the returned Task, which is wrong the vast majority of the time (hence the compiler warning). Specifically, ignoring the Task will ignore any exceptions from that Task. It's also not possible for your code to know when an ignored Task has completed, but presumably that's acceptable since your handler is returning void.
There is a "registration" that async void methods do so that the framework is aware there is a task still in progress, so the framework knows when it's safe to shut down. The only .NET provided framework that actually cares about that is ASP.NET pre-Core; all other .NET-provided frameworks (including all UI frameworks) ignore that "registration".
In this particular case, it depends mostly on your error handling.
If AwaitableMethod asynchronously throws, it will throw on the UI thread on the second version.
I would suggest to avoid event handlers in this case and use something like this instead if possible.
public class Eventful
{
private readonly List<Func<Task>> _handlers = new List<Func<Task>>();
public void AddHandler(Func<Task> handler) => _handlers.Add(handler);
private async Task RunHandlers()
{
foreach (var handler in _handlers)
{
await handler();
}
}
}
public class Consumer
{
public Consumer()
{
var eventful = new Eventful();
eventful.AddHandler(async () =>
{
await DoAsyncTask();
});
}
private static async Task DoAsyncTask()
{
await Task.CompletedTask;
}
}

ConfigureAwait(False) doesn't change context after ContinueWith()

I don't know if I am doing something wrong or I found a bug in the Async library, but I have seen an issue when running some async code after I came back to the Synchronized context with continueWith().
UPDATE: The code now runs
using System;
using System.ComponentModel;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
internal static class Program
{
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MainFrameController controller = new MainFrameController(this);
//First async call without continueWith
controller.DoWork();
//Second async call with continueWith
controller.DoAsyncWork();
}
public void Callback(Task<HttpResponseMessage> task)
{
Console.Write(task.Result); //IT WORKS
MainFrameController controller =
new MainFrameController(this);
//third async call
controller.DoWork(); //IT WILL DEADLOCK, since ConfigureAwait(false) in HttpClient DOESN'T change context
}
}
internal class MainFrameController
{
private readonly Form1 form;
public MainFrameController(Form1 form)
{
this.form = form;
}
public void DoAsyncWork()
{
Task<HttpResponseMessage> task = Task<HttpResponseMessage>.Factory.StartNew(() => DoWork());
CallbackWithAsyncResult(task);
}
private void CallbackWithAsyncResult(Task<HttpResponseMessage> asyncPrerequisiteCheck)
{
asyncPrerequisiteCheck.ContinueWith(task =>
form.Callback(task),
TaskScheduler.FromCurrentSynchronizationContext());
}
public HttpResponseMessage DoWork()
{
MyHttpClient myClient = new MyHttpClient();
return myClient.RunAsyncGet().Result;
}
}
internal class MyHttpClient
{
public async Task<HttpResponseMessage> RunAsyncGet()
{
HttpClient client = new HttpClient();
return await client.GetAsync("https://www.google.no").ConfigureAwait(false);
}
}
partial class Form1
{
private IContainer components;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Text = "Form1";
}
#endregion
}
}
The HttpClient code which is async runs well the first time.
Then, I run the second async code and return to the UI context with ContinueWith, and it works well.
I run the HttClient code again, but it deadlock because this time ConfigureAwait(false) does not change the context.
The main problem in your code is due to StartNew and ContinueWith. ContinueWith is dangerous for the same reasons that StartNew is dangerous, as I describe on my blog.
In summary: StartNew and ContinueWith should only be used if you're doing dynamic task-based parallelism (which this code is not).
The actual problem is that HttpClient.GetAsync doesn't use (the equivalent of) ConfigureAwait(false); it's using ContinueWith with its the default scheduler argument (which is TaskScheduler.Current, not TaskScheduler.Default).
To explain in more detail...
The default scheduler for StartNew and ContinueWith is not TaskScheduler.Default (the thread pool); it's TaskScheduler.Current (the current task scheduler). So, in your code, DoAsyncWork as it currently is does not always execute DoWork on the thread pool.
The first time DoAsyncWork is called, it will be called on the UI thread but without a current TaskScheduler. In this case, TaskScheduler.Current is the same as TaskScheduler.Default, and DoWork is called on the thread pool.
Then, CallbackWithAsyncResult invokes Form1.Callback with a TaskScheduler that runs it on the UI thread. So, when Form1.Callback calls DoAsyncWork, it is called on the UI thread with a current TaskScheduler (the UI task scheduler). In this case, TaskScheduler.Current is the UI task scheduler, and DoAsyncWork ends up calling DoWork on the UI thread.
For this reason, you should always specify a TaskScheduler when calling StartNew or ContinueWith.
So, this is a problem. But it's not actually causing the deadlock you're seeing, because ConfigureAwait(false) should allow this code to just block the UI instead of deadlocking.
It's deadlocking because Microsoft made the same mistake. Check out line 198 here: GetContentAsync (which is called by GetAsync) uses ContinueWith without specifying a scheduler. So, it's picking up the TaskScheduler.Current from your code, and will not ever complete its task until it can run on that scheduler (i.e., the UI thread), causing the classic deadlock.
There's nothing you can do to fix the HttpClient.GetAsync bug (obviously). You'll just have to work around it, and the easiest way to do that is to avoid having a TaskScheduler.Current. Ever, if you can.
Here's some general guidelines for asynchronous code:
Don't ever use StartNew. Use Task.Run instead.
Don't ever use ContinueWith. Use await instead.
Don't ever use Result. Use await instead.
If we just do minimal changes (replacing StartNew with Run and ContinueWith with await), then DoAsyncWork always executes DoWork on the thread pool, and the deadlock is avoided (since await uses the SynchronizationContext directly and not a TaskScheduler):
public void DoAsyncWork()
{
Task<HttpResponseMessage> task = Task.Run(() => DoWork());
CallbackWithAsyncResult(task);
}
private async void CallbackWithAsyncResult(Task<HttpResponseMessage> asyncPrerequisiteCheck)
{
try
{
await asyncPrerequisiteCheck;
}
finally
{
form.Callback(asyncPrerequisiteCheck);
}
}
However, it's always questionable to have a callback scenario with Task-based asynchrony, because Tasks themselves have the power of callbacks within them. It looks like you're trying to do a sort of asynchronous initialization; I have a blog post on asynchronous construction that shows a few possible approaches.
Even something really basic like this would be a better design than callbacks (again, IMO), even though it uses async void for initialization:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
MainFrameController controller = new MainFrameController();
controller.DoWork();
Callback(controller.DoAsyncWork());
}
private async void Callback(Task<HttpResponseMessage> task)
{
await task;
Console.Write(task.Result);
MainFrameController controller = new MainFrameController();
controller.DoWork();
}
}
internal class MainFrameController
{
public Task<HttpResponseMessage> DoAsyncWork()
{
return Task.Run(() => DoWork());
}
public HttpResponseMessage DoWork()
{
MyHttpClient myClient = new MyHttpClient();
var task = myClient.RunAsyncGet();
return task.Result;
}
}
Of course, there's other design problems here: namely, DoWork is blocking on a naturally-asynchronous operation, and DoAsyncWork is blocking a thread pool thread on a naturally-asynchronous operation. So, when Form1 calls DoAsyncWork, it's awaiting a thread pool task that's blocked on an asynchronous operation. Async-over-sync-over-async, that is. You may also benefit from my blog series on Task.Run etiquette.
Don't use .Result. If you have any code at all that uses async/await, just completely forget it even exists. Even if we get it working today, what you are trying to do will be so incredibly brittle that it won't necessarily work tomorrow.

Why is it not possible to have await operator in the constructor?

Just tried to call an async method with await property in the constructor, causing trouble:
public MainPage()
{
view = new View();
FacebookApiHandler FacebookApiHandler = new FacebookApiHandler(view);
await FacebookApiHandler.getAccessByLogin();
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
this.InitializeComponent();
}
So my 2 questions:
Why is it not possible to use an await call in the cunstructor?
Where else should the call be made?
Usually, if you are working with some kind of UI control as it appears you are, if you have some async work that needs to be done you can put it in the Loaded event or some other similar equivalent.
Another option is you start the asynchronous action in the constructor and keep a reference in a Task then in your method that needs that async work you perform your await there. If the task was not done it returns to the caller to be awaited and it the task was done it just continues synchronously.
The reason you can not await in a constructor is the object the constructor is building must be ready to be assigned at the end of the constructor, they did not want to allow you to have a "half finished" object because you would then need a way to track if it was fully complete.
If you really need the class in the constructor one last work around is you have a static factory method make your object. This really is just hiding a implementation of option #2.
public MainPage : Page
{
protected MainPage()
{
view = new View();
_initTask = Init()
}
private async Task Init()
{
FacebookApiHandler FacebookApiHandler = new FacebookApiHandler(view);
await FacebookApiHandler.getAccessByLogin();
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
this.InitializeComponent();
}
private Task _initTask.
public static Task<MainPage> CreatePageAsync()
{
var page = new MainPage();
await page._initTask;
return page;
}
}
//Elsewhere
private async Task MyCode()
{
MainPage page = await MainPage.CreatePageAsync();
}
Constructors are supposed to immediately create an object. By using await, it is not certain when the operation will finish. It is not even certain that it will finish at all.
You class seems to be a GUI page. These normally have an event like Initialized. You should put your asynchronous code there.

How do I implement InvokeRequired UI pattern inside an async method?

Let's say I have a Form that tries to deal with a multi-threaded environment; it therefore checks if it's running on the UI thread before any UI modification is done:
partial class SomeForm : Form
{
public void DoSomethingToUserInterface()
{
if (InvokeRequired)
{
BeginInvoke(delegate { DoSomethingToUserInterface() });
}
else
{
… // do the actual work (e.g. manipulate the form or its elements)
}
}
}
Now let's say I am performing some lengthy operation inside the … part of that method; I'd therefore like to make it asynchronous using async/await.
Given that I should change the method signature to return a Task instead of void (so that exceptions can be caught), how would I implement the part that performs the BeginInvoke? What should it return?
public async Task DoSomethingToUserInterfaceAsync()
{
if (InvokeRequired)
{
// what do I put here?
}
{
… // like before (but can now use `await` expressions)
}
}
When using async-await with a custom awaiter such as the common TaskAwaiter, the SynchronizationContext is being implicitly captured for you. Later, when the asynchronous method completes, the continuation (any code after the await) is being marshaled back to that same sync context using SynchronizationContext.Post.
This altogether eliminates the need to use InvokeRequired and other techniques used to mainpulate work on the UI thread. In order to do that, you'll have to trace your method calls all the way to the top level ones and refactor them to use async-await probably.
But, to address the specific problem as is, what you can do is capture the WinFormSynchronizationContext when your Form initializes:
partial class SomeForm : Form
{
private TaskScheduler _uiTaskScheduler;
public SomeForm()
{
_uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
}
And later use it when you want to await:
if (InvokeRequired)
{
Task uiTask = new Task(() => DoSomethingToUserInterface());
uiTask.RunSynchronously(_uiTaskScheduler);
}
else
{
// Do async work
}
You can use TaskScheduler.FromCurrentSynchronizationContext to get the task scheduler for current synchronization context(For UI thread), and store it in a field for later use.
Then when you're interested to start any task in UI thread, you have to pass the uiScheduler to the StartNew method, so that TPL will schedule the task in the provided scheduler(UI thread in this case).
Anyway you decided to run the stuff in UI thread, so just schedule it to UIScheduler, you don't need to check for InvokeRequired whatsoever.
public async Task DoSomethingToUserInterfaceAsync()
{
await Task.Factory.StartNew(() => DoSomethingToUserInterface(), CancellationToken.None, TaskCreationOptions.None, uiScheduler);
...
}
To retrieve the UI scheduler, you can use the following code
private TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Note: It is extremely important that TaskScheduler.FromCurrentSynchronizationContext should be called only from UI thread, otherwise, it will throw exception, or you'll get a TaskScheduler for some other SynchronizationContext which won't do what you need.
Also note that if you have started the async operation from UI thread itself, you don't need any of the above magic. await will resume in the context where it has been started.

Categories

Resources