Is it safe to change ui properties after await in async methods? - c#

I was messing up with c#'s await/async when I wrote the following program:
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Test();
while (true) { }
}
static async void Test()
{
var t = Task.Run(()=> Thread.Sleep(1000));
await t;
throw new Exception("This exception happens in a worker thread");
}
}
When I run this program, I can clearly see in Visual Studio's thread window that the exception happens in a Worker Thread, rather than the Main Thread, this leads me to believe that when I await a task, my method can be finished by another thread.
However, when I open a new windows forms application and add the following event handler:
async void button1_Click(object sender, EventArgs e)
{
var task = Task.Run(() => Thread.Sleep(1000));
await task;
button1.Text = "I'm in the main thread!"; //this works
}
This works, so I notice that in a console application my Test function is resumed by a worker thread, while in a Windows Forms application, it's always resumed by the main thread.
Why is this? Is it reliable?

Yes. It is reliable.
When you do await, the current SynchronizationContext is captured so that when it continues after the await, this context will be used to execute the code.
In GUI applications, the current SynchronizationContext is a context that will execute code on the UI thread.
In Console Applications, the current SynchronizationContext is null, therefore the Task continues on a thread-pool thread.
Take a look at this blog post.

As I explain in my async intro, by default await will capture a "context" and resume execution of its async method in that context.
Technically, this context is SynchronizationContext.Current unless it is null, in which case it is TaskScheduler.Current.
In everyday terms, this means that the context is a UI context if the method is running on a UI thread; it is an ASP.NET request context if the method is servicing an ASP.NET request; and it is most likely the thread pool context in all other situations.
Note that this is default behavior. You can specify that the method does not need to resume on its context by awaiting the result of ConfigureAwait(continueOnCapturedContext: false). In this case, it is likely that the rest of the method will be executed on a thread pool thread; but technically this just means that the method will be executed "somewhere" and you don't care where.

Yes, this is correct and one of the intended uses of async / await.
The Task that you are awaiting is what happens asynchronously. When it resumes, it resumes on the calling thread, which will be the UI thread in the case of a WinForms event handler.
Note that it is possible for you to change this behavior using Task.ConfigureAwait(False). When the task is configured this way, it will not pass control back to the original thread and will instead resume on the task's thread.
All of this behavior is dependant on the application's current SynchonizationContext though. Some application types will have different contexts which can subtly change the behaviors. Console Applications are a good example of this, as they do not set a synchronization context by default, and will resume on the Task's thread. You can change this if you want by creating a context for the console app, as shown here.

Related

Why this async method don't cause deadlock on a thread?

I need an explanation why this code doesn't cause deadlock on the thread it's running in
(It's a WinForm application and these happens in button_Click):
Task FakeMainThread = Task.Run(async() => // fake main thread
{
async Task asyncMethod() // executed in FakeMainThread
{
await Task.Run(() => // 'await'ing in FakeMainThread
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock should appear in FakeMainThread
MessageBox.Show("FakeMainThread finished");
return 4;
});
asyncMethod().Wait(); should block the fakeMainThread Task's thread while it's await another task to finish and since await is happening in fakeMainThread too it shouldn't be able to 'await' and deadlock should appear on fakeMainThread. But it doesn't happen and I see bot MessageBox.Show("asyncMethod finished"); and MessageBox.Show("FakeMainThread finished"); messages.
Note: if I put this:
async Task asyncMethod()
{
await Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("child Task finished");
return 3;
});
MessageBox.Show("asyncMethod finished");
}
asyncMethod().Wait(); // deadlock appears in Main Thread (UI)
MessageBox.Show("FakeMainThread finished");
in main button1_click() inside directli deadlock appears on UI. Thanks!
You don't experience a deadlock in the first code example because of the SynchronizationContext. This context says what the await must do to restore your code. When you start a new task (Task.Run), you will get the default context. In the button1_click you get the context from the form.
The context for the form allows to execute only a single thread (the one used to paint/update your form).
When you call .Wait(), then you keep that thread 'locked' with waiting until the task is done. That means that the thread is not released for another job and this is one of the reasons that the form goes into the 'not responding' state.
When you do an await [code], and that code is done, then it will ask the synchronization context to schedule the remainder of the code.
In case of the default context, any free thread is taken and your code will continue, the task is marked as done, and thus the 'loop' in the .Wait() gets signaled that the task is done and continues.
In case of the forms context, there is only a single thread, thus the completion of this task is scheduled to run after the current code is done. This is the reason of the deadlock.
Avoiding this deadlock is the main reason that you most often get the suggestion to use ConfigureAwait(false), this tells the await that it should substitute the current synchronization context with the default one, thus allowing you to use any free thread.
The only reason you don't want to use this (or say ConfigureAwait(true)) is if you need to update something on your form (WPF or WinForms) or you need your http context (ASP.NET, not ASP.NET Core). This is because only a single thread has access to your form or http context. Here you don't get an exception on your MessageBox.Show because this is 'outside' of the forms context and doesn't need this special thread/synchronization context.
See https://devblogs.microsoft.com/dotnet/configureawait-faq/ for more information.

ConfigureAwait(false) doesn't make the continuation code run in another thread

I have a code which runs on thread1.
I call a function in a synchronic way (using async method but it shouldn't disturb me - async method doesn't turn code to be asynchronic).
I have an await with ConfigureAwait set to false, so I understood the
code after it is a task continuation which suppose to run in a different thread than the code before the await (because ConfigureAwait was set to false).
By my test - All code run in the same thread.
How is it? Why doesn't the code, below the await, run on a different thread?
This is the code:
public async void F1()
{
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
int x = await F2().ConfigureAwait(false);
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
}
private async Task<int> F2()
{
Console.WriteLine("Begins F2");
Console.WriteLine($"Thread.CurrentThread.ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine("Finishes F2");
return 7;
}
This is the output:
Thread.CurrentThread.ManagedThreadId=1
Begins F2
Thread.CurrentThread.ManagedThreadId=1
Finishes F2
Thread.CurrentThread.ManagedThreadId=1
It is not "supposed to run in a different thread context", it is allowed to. In the opposite case (ConfigureAwait(true)) it must continue on the same thread context.
Furthermore when there is nothing to await (inside the method), the "async" method runs synchronously so doesn't need to return to some thread context, it is still on it.
I have an await with ConfigureAwait set to false, so I understood the code after it is a task continuation which suppose to run in a different thread than the code before the await (because ConfigureAwait was set to false).
No. There are a couple of misunderstandings here.
The first misunderstanding is regarding what ConfigureAwait(false) does.
ConfigureAwait (and await) have nothing to do with threading directly. By default, await captures a context - the current SynchronizationContext or TaskScheduler. This context could resume on the same thread (e.g., UI SynchronizationContexts commonly do this), but "context" does not necessarily mean "thread" (e.g., the ASP.NET pre-Core SynchronizationContext can resume on any thread pool thread).
What ConfigureAwait(false) actually does is skip capturing that context. So the thread pool context is used, which may run on any thread pool thread. Note that if the code before await was running on a thread pool thread, it may resume on any thread pool thread, including the thread that it was running on before.
The second misunderstanding is regarding when ConfigureAwait(false) is applied.
await will first check to see if its awaitable is complete, and only then will it actually behave asynchronously. So if you await an already-completed task, the ConfigureAwait(false) is never even considered - the code just continues running synchronously.
How can I enforce it to run on a different thread then?
Use Task.Run. Task.Run is the proper tool to use when you need to run code on a thread pool thread.

ExcelDna: Async: The calling thread must be STA

I'm working with ExcelDna and async functions. If there's an exception in the async:d code I want to show a fancy WPF error window. My problem is that I'm getting the error "The calling thread must be STA, because many UI components require this." How can I solve this?
[ExcelFunction(Description = "", Category = "")]
public static async Task<object> /*string*/ Foo(CancellationToken ct)
{
try
{
return await Task.Run(async () =>
{
await Task.Delay(1000, ct);
throw new Exception("BOO");
return "HelloWorld";
}, ct2.Token);
}
catch (Exception e)
{
return ShowWpfErrorWindowThatRequiresSTA(e);
}
}
When your Excel function runs there is no SynchronizationContext.Current installed, so the async/await mechanism will runs the code after await (including your catch handler) on a ThreadPool thread. That's not a context where you can directly show your WPF form.
Installing a DispatcherSynchronizationContext corresponding to a Dispatcher running on the main thread (or another thread) would work, but you have to do that for every UDF call. Somehow the native code path through Excel loses the .NET call context on the main thread, so the SynchronizationContext gets lost.
Better is probably to assume that the catch handler is running on a ThreadPool thread, and make a SynchronizationContext.Post call from the catch handler to take you back to the main thread running your Dispatcher and WPF form.
You can look at how Excel-DNA implements the (WinForms) LogDisplay window. (https://github.com/Excel-DNA/ExcelDna/blob/master/Source/ExcelDna.Integration/LogDisplay.cs). You can call LogDisplay.WriteLine(...) from any thread, and it will do a _syncContext.Post to run the 'Show' on the main thread.
The C# async/await mechanism works less well with Excel since the native/managed transitions, and whatever Excel does internally, messes up the thread context that needs to flow between continuations. Even on the .NET side, it's not clear how thread context is managed between AppDomains (different Excel add-ins). So it's best not to rely on the .NET runtime being able to thread any kind of context through the managed/native transitions.
Many Office plugins have a problem where SynchronizationContext.Current is null, and asynchronous continuations execute on the thread pool. I'd check the value of SynchronizationContext.Current before the first await.
I have had some success in creating a WinFormsSynchronizationContext and installing that on the thread before the first await. However, installing a WPF context would be more complex.

Is Async/Await using Task.Run starting a new thread asynchronously?

I have read a lot of articles and still cant get understand this part.
Consider this code :
private async void button1_Click(object sender, EventArgs e)
{
await Dosomething();
}
private async Task<string> Dosomething()
{
await Task.Run((() => "Do Work"));
return "I am done";
}
First question:
When I click the button, it will Call DoSomething and await a Task that creates a Thread from the threadpool by calling Task.Run ( if I am not mistaken ) and all of this runs asynchronously. So I achieved creating a thread that does my work but doing it asynchronously? But consider that I don't need any result back, i just want the work to be done without getting any result back, is there really a need to use async/await , and if so, how?
Second question:
When running a thread asynchronously, how does that work? Is it running on the main UI but on a separate thread or is it running on a separate thread and separate is asynchronously inside that method?
The purpose of creating Async methods is so you can Await them later. Kind of like "I'm going to put this water on to boil, finish prepping the rest of my soup ingredients, and then come back to the pot and wait for the water to finish boiling so I can make dinner." You start the water boiling, which it does asynchronously while you do other things, but eventually you have to stop and wait for it. If what you want is to "fire-and-forget" then Async and Await are not necessary.
Simplest way to do a fire and forget method in C#?
Starting a new task queues that task for execution on a threadpool thread. Threads execute in the context of the process (eg. the executable that runs your application). If this is a web application running under IIS, then that thread is created in the context of the IIS worker process. That thread executes separately from the main execution thread, so it goes off and does its thing regardless of what your main execution thread is doing, and at the same time, your main execution thread moves on with its own work.
1
There's a big difference if you don't await the Task or you await it:
Case you don't await it: DoSomething is called but next sentence is executed while DoSomething Task hasn't been completed.
Case you await it: DoSomething is called and next sentence is executed once DoSomething Task has been completed.
So, the need of async/await will depend on how you want to call DoSomething: if you don't await it is like calling it the fire & forget way.
2
Is it running on the main UI but on a separate thread or is it running
on a seperate thread and separate is asynchronously inside that
method?
Asynchronous code sometimes means other thread (see this Q&A Asynchronous vs Multithreading - Is there a difference?). That is, either if the code is being executed in a separate thread from the UI one or it lets continue the processing of the UI thread while it gets resumed, it's nice because UI loop can still update the screen while other tasks are being done in parallel without freezing the UI.
An asynchronous method (i.e. async method) is a syntactic sugar to tell the compiler that await statements should be treated as a state machine. The C# compiler turns your async/await code into a state machine where code awaiting a Task result is executed after the code that's being awaited.
Interesting Q&As
You might want to review these other Q&As:
Async/Await vs Threads
What's the difference between Task.Start/Wait and Async/Await?
async/await - when to return a Task vs void?
Is Async await keyword equivalent to a ContinueWith lambda?
OP said...
[...] But does this mean that "async/await" will fire off a thread and
Task.Run also fires off a thread or are they both the same thread?
Using async-await doesn't mean "I create a thread". It's just a syntactic sugar to implement continuations in an elegant way. A Task may or may not be a thread. For example, Task.FromResult(true) creates a fake task to be able to implement an async method without requirement it to create a thread:
public Task<bool> SomeAsync()
{
// This way, this method either decides if its code is asynchronous or
// synchronous, but the caller can await it anyway!
return Task.FromResult(true);
}
The type Task<TResult> requires you to return a TResult from your task. If you don't have anything to return, you can use Task instead (which, incidentally, is the base class of Task<TResult>).
But keep in mind that a task is not a thread. A task is a job to be done, while a thread is a worker. As your program runs, jobs and workers become available and unavailable. Behind the scenes, the library will assign your jobs to available workers and, because creating new workers is a costly operation, it will typically prefer to reuse the existing ones, through a thread pool.

Call to await GetFileAsync() never returns and app hangs in WinRT app

I'm attempting to load and read a settings file on application launch, and about 90% of the time, the await GetFileAsync("filename.xml"); never returns, thus, hanging the application.
About a quarter of the time, if I step through the code, it'll actually return and read the file.
Here's a very simplified version of the code:
App.xaml.cs:
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
FileLoader.Load().Wait();
// File-load dependent stuff
}
FileLoader.cs:
public async static Task Load()
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file;
bool fileExists = true;
try
{
// The following line (often) never returns
file = await folder.GetFileAsync("filename.xml");
{
catch
{
fileExists = false;
}
// Do stuff with loaded file
}
If I watch the Output window in Visual Studio, after awhile of waiting I get "The thread '<No Name>' (0x30c) has exited with code 0 (0x0)."
Does anyone have any idea of what's happening here?
By default, when you await a Task that has not yet completed, the method resumes on a captured context (in this case, the UI context).
So, here's why your code is failing:
OnLaunched calls Load (within the UI context).
Load awaits. This causes the Load method to return an incomplete task and schedule its completion for later. This continuation is scheduled for the UI context.
OnLaunched blocks on the task returned from Load. This blocks the UI thread.
GetFileAsync eventually completes, and attempts to run the continuation for Load.
The continuation for Load waits for the UI thread to be available so it can execute in the UI context.
At this point, OnLaunched is waiting for Load to complete (blocking the UI thread by doing so), and Load is waiting for the UI thread to be free. Deadlock.
These best practices avoid this situation:
In your "library" async methods, use ConfigureAwait(false) whenever possible. In your case, this would change await folder.GetFileAsync("filename.xml"); to await folder.GetFileAsync("filename.xml").ConfigureAwait(false);.
Don't block on Tasks; it's async all the way down. In other words, replace Wait with await.
For more information:
Await, and UI, and deadlocks! Oh, my!
My async/await intro post, which includes a brief description of how Task awaiters use SynchronizationContext and introduces some best practices.
The Async/Await FAQ, which goes into more detail on the contexts.
This MSDN forum post.
Stephen Toub demos this deadlock, and so does Lucian Wischik.
Update, 2012-07-13: Incorporated this answer into a blog post.

Categories

Resources