Return async result to an event - c#

Suppose I had and API event, where I had to return a result.
The computation I have to do uses an async method, which creates a control (must be run on the UI thread).
private async void API_QueryControl(object sender, APIEventArgs e)
{
e.Result = await CreateControl();
}
private async Task<Control> CreateControl()
{
await Task.Delay(1000);
return new Panel();
}
Now, this is obviously not going to work, since API_QueryControl yields before I set the result. I have no control over the API, which means the event cannot be changed. How would I get the event to wait for the result?

You can block the current (UI) thread, but avoid a deadlock, by using ConfigureAwait(false). Like this:
e.Result = await CreateControl().ConfigureAwait(false);
ConfigureAwait(false) makes the continuation part of CreateControl - the part after await Task.Delay - run on the threadpool instead of the original UI thread.
I assume you have some reason for using async like this, instead of just writing a synchronous version of CreateControl. That would not only be simpler code, it would also use the original (UI) thread to do the work, instead of using a second thread while the UI thread waits for it.
Also note that you should always prevent exceptions from being thrown from async void methods - see the accepted answer here.

Related

Async action hangs/deadlocks, why? (not waiting for Result, nor ConfiguringAwait)

First, sorry for yet another "why my async action hangs" question but I believe this question is different enough.
Surveying dozens of similar questions, the problem of async action deadlock is either in locking yourself out (.Result), using limited resources or using library components incorrectly (web requests seems popular). In the following example, I cannot find any from above:
private async Task ExecuteAsync(Task<int> task)
{
// entering on current thread, that is the main UI thread
await task // execute "task" asynchronnously (on a different thread)
.ConfigureAwait(false); // when done, no need to return to main thread
MessageBox.Show("success"); // succes indicator
}
public MainWindow() //wpf window ctor
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var task = new Task<int>(() => 42); // make an Action wrapping sychronnous method
// fire and forget, never caring for .Result, disard even the task
var _ = ExecuteAsync(task).ConfigureAwait(false);
}
I have commented the sample with my best try on exaplaining how things (should) work, but something in my explanation must be wrong. Even though MainWindow ctor does not deadlock, the Action () => 42 is never executed and "success" message is not shown. After some debugging I managed to fix the sample (using Task.FromResult), but I am still not sure what is wrong with it as it is now and even more importantly why.
What is the error in my reasoning and why was the action never executed/finished?
You did not start the task! You only declared it. Simply awaiting it does not "fire" it.
private async Task ExecuteAsync(Task<int> task)
{
// at first do start the task
task.Start();
await task.ConfigureAwait(false);
MessageBox.Show("success");
}
Note that ConfigureAwait(false) does not guarantee that execution will be continued on a different thread. It only says that you don't need it to be resumed on the original thread. And resuming UI work (like MessageBox.Show()) on a non-UI thread is not recommended.
As NineBerry pointed out, if you want to wrap a synchronous method and let it run on a different thread, you should use Task.Run():
var task = Task.Run(() => YourSynchronousCall());

Understanding async / await and Task.Run()

I thought I understood async/await and Task.Run() quite well until I came upon this issue:
I'm programming a Xamarin.Android app using a RecyclerView with a ViewAdapter. In my OnBindViewHolder Method, I tried to async load some images
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
// Some logic here
Task.Run(() => LoadImage(postInfo, holder, imageView).ConfigureAwait(false));
}
Then, in my LoadImage function I did something like:
private async Task LoadImage(PostInfo postInfo, RecyclerView.ViewHolder holder, ImageView imageView)
{
var image = await loadImageAsync((Guid)postInfo.User.AvatarID, EImageSize.Small).ConfigureAwait(false);
var byteArray = await image.ReadAsByteArrayAsync().ConfigureAwait(false);
if(byteArray.Length == 0)
{
return;
}
var bitmap = await GetBitmapAsync(byteArray).ConfigureAwait(false);
imageView.SetImageBitmap(bitmap);
postInfo.User.AvatarImage = bitmap;
}
That pieces of code worked. But why?
What I've learned, after configure await is set to false, the code doesn't run in the SynchronizationContext (which is the UI thread).
If I make the OnBindViewHolder method async and use await instead of Task.Run, the code crashes on
imageView.SetImageBitmap(bitmap);
Saying that it's not in the UI thread, which makes totally sense to me.
So why does the async/await code crash while the Task.Run() doesn't?
Update: Answer
Since the Task.Run was not awaited, the thrown exception was not shown. If I awaitet the Task.Run, there was the error i expected. Further explanations are found in the answers below.
It's as simple as you not awaiting the Task.Run, so the exception gets eaten and not returned to the call site of Task.Run.
Add "await" in front of the Task.Run, and you'll get the exception.
This will not crash your application:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => { throw new Exception("Hello");});
}
This however will crash your application:
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => { throw new Exception("Hello");});
}
Task.Run() and the UI thread should be used for a different purpose:
Task.Run() should be used for CPU-bound methods.
UI-Thread should be used for UI related methods.
By moving your code into Task.Run(), you avoid the UI thread from being blocked. This may solve your issue, but it's not best practice because it's bad for your performance. Task.Run() blocks a thread in the thread pool.
What you should do instead is to call your UI related method on the UI thread. In Xamarin, you can run stuff on the UI thread by using Device.BeginInvokeOnMainThread():
// async is only needed if you need to run asynchronous code on the UI thread
Device.BeginInvokeOnMainThread(async () =>
{
await LoadImage(postInfo, holder, imageView).ConfigureAwait(false)
});
The reason why it's working even if you don't explicitly call it on the UI thread is probably because Xamarin somehow detects that it's something that should run on the UI thread and shifts this work to the UI thread.
Here are some useful articles by Stephen Cleary which helped me to write this answer and which will help you to further understand asynchronous code:
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html
https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html
Probably UI access still throws UIKitThreadAccessException. You do not observe it because you do not use await keyword or Task.Wait() on a marker that Task.Run() returns. See Catch an exception thrown by an async method discussion on StackOverflow, MSDN documentation on the topic is a bit dated.
You can attach continuation to the marker that Task.Run() returns and inspect exceptions thrown inside an action passed:
Task marker = Task.Run(() => ...);
marker.ContinueWith(m =>
{
if (!m.IsFaulted)
return;
// Marker Faulted status indicates unhandled exceptions. Observe them.
AggregateException e = m.Exception;
});
In general, UI access from non UI thread may make an application unstable or crash it, but it isn't guaranteed.
For more information check How to handle Task.Run Exception, Android - Issue with async tasks discussions on StackOverflow, The meaning of TaskStatus article by Stephen Toub and Working with the UI Thread article on Microsoft Docs.
Task.Run is queuing LoadImage to execute the async process on the thread pool with ConfigureAwait(false). The task that LoadImage is returning is NOT awaited though and I believe that is the important part here.
So the results of Task.Run is that it immediately returns a Task<Task>, but the outer task does not have ConfigureAwait(false) set, so the whole thing resolves on the main thread instead.
If you change your code to
Task.Run(async () => await LoadImage(postInfo, holder, imageView).ConfigureAwait(false));
I'm expecting you to hit the error that the thread isn't running on the UI thread.

Do I need invoke to access DataGridView.SelectedRows from a async method?

I have a method like this:
async Task foo() {
foreach (DataGridViewRow row in dataGridView1.SelectedRows) {
// ...
}
}
called like this:
await Task.Run(() =>
{
foo();
});
I just noticied that code is acessing dataGridView1.SelectedRows directly, without invoke and it's working fine. Am I performing an invalid operation? Is this supposed to work or must I use invoke here?
The official answer is that is depends on who calls your foo function. Is it the main thread, or could it be a different thread?
If it is the main thread (better: the thread that created the control), than you don't need an Invoke. Async does not influence this
The following is done by the UI thread, and will work fine.
public async void Button1_Clicked(object sender, ...)
{
await Foo();
}
Quite often people think that async-await is done by several threads. But in fact it is not. A thread in an async function performs all statements until it meets an await. Instead of really waiting until the awaited function completes, it goes up in its call stack to see if it can do something else.
This is perfectly explained in Eric Lippert's restaurant metaphor (search on the page for async). Instead of waiting until the bread is toasted, the cook starts boiling eggs. But it is still the same cook.
When you see code where an async function is called without await, the thread will do the call until it meets an await and will perform the statements after the not-awaited call instead of doing nothing.
private async void Button1_clicked(object sender, ...)
{
var taskFoo = this.Foo()
// because not await: the thread will do the things in Foo until it meets
// an await. Then it returns back to do the next statements:
DoSomething();
// if this function has nothing more to do, or if it needs the result
// of Foo, await Foo:
await taskFoo;
ProcessFooResult();
}
This await for taskFoo has the effect control is given back to my caller (still the same thread), until my caller awaits. In that case control is given to his caller until await etc.
The only time a different thread is involved is when you actively start it, usually using:
var myTask = Task.Run( () => DoSomething() );
// Because there is no await, your thread will do immediately the next
// statements until an await:
DoOtherThings();
await myTask();
Now DoSomething is performed by a different thread. If you need to access UI controls you'll need InvokeRequired and Invoke.
Another helpful story about async-await: Stephen Cleary about async-await
It's because you use async and await instead of creating a new Thread / BackgroundWorker.
In my opinion it is never bad to use Invoke when interacting with Controls.

Why can new threads access the UI?

Given the following method as example:
private async void F()
{
button.IsEnabled = false;
await Task.Delay(1000);
button.IsEnabled = true;
}
In this case, any code starting at await always occurs on another thread (edit: wrong) which presumably should not have access to the UI thread, similarly to desktop apps. In a similar situation, I recall having an exception such as:
The application called an interface that was marshalled for a different thread.
However, the example does not trigger any exception. Is this expected? Can I reliably write code like this?
any code starting at await always occurs on another thread (non-UI thread, right?),
No, not at all. await does not kick off other threads. I have an async intro that may help if you find this statement confusing.
What await will do is schedule the remainder of the method as a continuation to be run after the asynchronous operation completes (in this case, the asynchronous operation is just a timer firing). By default, await will capture a "context", which is SynchronizationContext.Current (or, if it is null, the context is TaskScheduler.Current). In this case, there's a UI SynchronizationContext that ensures the remainder of the async method will run on the UI thread.
Code running on the UI thread has a SynchronizationContext. You can see that by printing SynchronizationContext.Current. Before you await something that context is captured and after the await your code resumes on that context which makes sure the continuation runs on the UI thread.
To get the behavior you're referencing, where the continuation is run on a ThreadPool thread you can disable the SynchronizationContext capturing by using ConfigureAwait(false):
private async void FooAsync()
{
button.IsEnabled = false;
await Task.Delay(1000).ConfigureAwait(false);
button.IsEnabled = true;
}
This code will raise the exception you expect.
Is this expected? Can I reliably write code like this?
Yes and yes. Code using async-await will "do the right thing" by default. But if you do want to offload something to a ThreadPool thread you can use Task.Run.

Equivalent of F# StartImmediate

Is there a C# equivalent of F#'s StartImmediate which can be executed on a Task? I have a situation where I need to await a function sometimes, but other times I just want the method to call back to the UI thread without using await.
I want to avoid async void if possible.
Example:
public async void DoSomething()
{
//before runs on UI thread
await OtherThing();
//after runs on UI thread
}
public async Task DoSomethingElse()
{
//before runs on UI thread
await OtherThing();
//I want this to run on the UI thread
}
public void CallDoSomething()
{
DoSomething(); //no need to await, returns to the UI thread to continue execution
DoSomethingElse();//Doesn't return to the UI thread to continue execution
DoSomethingElse().???(); // how to return to the UI thread for the async callback?
}
In F# this would look like this:
let CallDoSomething () =
//more code here
async {
//runs on the current thread
do! OtherThing()
//runs on the current thread
} |> Async.StartImmediate
//code here doesn't wait for the async to finish
So, when looking at your code example. You have an async method that return a Task. You're calling that method, and then calling Start on the resulting task (without calling await). That will throw an exception at runtime. The task is already started just by calling the method. Starting an already running task (or a completed task, if it completed before it returned) will throw an exception. You can only Start a task created using the Task constructor, which you're not using.
You also claim, in comments, that you are not in the UI thread when you return after calling DoSomethingElse. That is false. The code will run in the UI thread.
You also imply that the await in DoSomethingElse won't marshal the callback to the UI thread, but it will. Since it was called from the UI thread, it captured the UI context, and uses it when calling await.
So in short, you need to do nothing except remove the call to Start.
The one problem you do have, if you structure your code like this, is that you will never know if your code errors out, as there is nothing to observe any exceptions. There are two ways you could address this. One would be for each of the methods you call in this manor to consider themselves "top level methods" and handle all exceptions instead of every throwing any.
The other option would be to store all of the tasks called in this manner and await Task.WhenAll those tasks, so you can observe any errors when they all finish.
Did you try the RunSynchronously() method of the Task class ?

Categories