Before I use
protected async void OnResume() {
await DoWorkAsync(); // assume exception is appropriately handled
await DoOtherWorkAsync();
}
But now I saw other developers use
protected void OnResume() {
SynchronizationContext.Current.Post(async (status) => {
await DoWorkAsync();
await DoOtherWorkAsync();}, null);
}
Is the second one correct and preferred when calling async methods inside non-async methods? Thank you in advance.
UPDATE:
If you use the 2nd snippet, you must call SynchronizationContext.Current.Post() on UI Thread, or you will get NullReferenceException, cause SynchronizationContext.Current is null on non-UI Thread. _handler.Post(async() => {// do UI changes}); can be called on non-UI Thread to do any UI changes, as long as _handler is instantiated on UI Thread.
No, don't use SynchronizationContext. You're already on the UI thread, so the second option really makes no sense.
Note that normally async void should be avoided. In this case, I'd say it's acceptable because you're actually treating OnResume as though it were an event handler:
protected async void OnResume()
{
await DoWorkAsync();
}
Both examples are performing unnecessary work. The best implementation of the method would be:
protected void OnResume()
{
DoWorkAsync();
}
In the first case you're forcing a continuation to run in the current context but that continuation has nothing to actually do; it's just pointlessly adding a post to the synchronization context.
The second snippet is adding two pointless posts two the current synchronization context, one explicitly, and one implicitly.
Both methods also have async methods that are adding state machines, even though you don't actually leverage any of that functionality, so that overhead is all entirely wasted.
Related
I'm trying to check if boolean is true and if it's false keep checking until it's true. I find this and i don't really understand the example. I don't find anything and i try to do it easily but it isn't working it's freezing.
private void dashboard_Paint(object sender, PaintEventArgs e)
{
if (IsLogged().GetAwaiter().GetResult())
{
Console.WriteLine("Logged");
}
}
public async Task<Boolean> IsLogged()
{
while (!logged)
{
if (logged)
return true;
await Task.Delay(25);
}
return false;
}
Tasks in C# are a way to implement cooperative multitasking, where a number of tasks can potentially run on the same thread, each doing small amounts of work before yielding the cpu for other tasks to execute. If one of the tasks misbehaves and doesn't yield back to the task scheduler then the thread is blocked and no tasks will run until the block is cleared. When the task is invoked again it continues where it left off.
The default task scheduler runs tasks on a thread pool which helps to mitigate (but not eliminate) the impact of thread blocking. On WinForms though the task scheduler is set up to run tasks on the UI thread by default, since so many operations can only be done from the main thread. Overall this is a good thing, but it leads to problems whenever thread blocking comes up. Instead of blocking one of a group of thread pool threads, you're blocking the thread your UI and all of your other threads are running on.
The way to deal with this is to use async and await everywhere that it makes sense to do so. If you're ever waiting on a task to finish, use await to do it. If you ever find yourself using .Result, .Wait() or .GetAwaiter().GetResult() then ask yourself whether it can be rewritten as an async method, even if you have to use async void for event handlers.
For your paint event that means doing this:
private async void dashboard_Paint(object sender, PaintEventArgs e)
{
if (await IsLogged())
Console.WriteLine("Logged");
}
(While you shouldn't use async void in general, this is a requirement for async event handlers since the event signature is delegate void X(...) not delegate Task X(...).)
I won't get into why you should never have a wait in an OnPaint event handler, or why your IsLogged example has issues (and should probably be called WaitLogged instead), but there's one thing that you might want to be aware of just in case: the volatile keyword.
Assuming that logged is a field rather than a property, the optimizer can capture the value of the field and continue to use that captured value for the life of the method. The volatile keyword tells the optimizer that this shouldn't happen, and that every reference to logged should always result in a read from the variable rather than the captured value.
In the worst case you can end up with optimized code that looks something like this:
private async Task<bool> IsLogged()
{
if (!logged)
{
while (true)
await Task.Delay(25)
}
return true;
}
From the optimizer's point of view, that's what your code does. It doesn't know that the logged value can be changed by something else, so it doesn't consider that options. Adding volatile lets it know that it can't do that.
private async Task dashboard_Paint(object sender, PaintEventArgs e)
{
if (await IsLogged())
{
Console.WriteLine("Logged");
}
}
That should solve the issue with the freezing because GetAwaiter() sometimes freezes if not used correctly.
Have you tried checking to see what is setting the logging boolean to true? It seems like an infinite loop.
I have a server-side click event on a ASP.NET WebForms website. In this event I call a method which in turn calls its async partner method, adding .Wait() on the call.
This method then goes several levels down (i.e., calls another async method, which calls another async method, and so on) and eventually calls an async method on an HttpClient object. At this point the thread seems to disappear down a rabbit hole; the method never calls back.
Now, I know that the async series of calls works as expected because the same code is also called from a Web API controller (the controller method calls the async version of that first method, not the synchronous 'partner' method), which is fully async, and it returns as expected.
So basically I have something like this, which never returns
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Class1.DoSomethingAsync.Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // never returns
}
I did try using .ConfigureAwait(false) on that first async method but without any success.
I also have this, which does return
Task<IHttpActionResult> MyWebApiMethod()
> await Class1.DoSomethingAsync()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync() // does return
}
I've found that I can make the first version work if I change it to the following:
protected void btn_Click(object sender, EventArgs e)
> Class1.DoSomething()
> Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
...
> await ClassN.Authenticate()
{
await myHttpClient.PostAsync()
}
But I don't know why.
Can anyone explain the difference between calling
Class1.DoSomethingAsync.Wait()
and calling
Task.Run(async () => await Class1.DoSomethingAsync()).Wait()
I explain this behavior in my blog post Don't Block on Asynchronous Code and my MSDN article on asynchronous best practices.
the thread seems to disappear down a rabbit hole; the method never calls back.
This happens because one of the awaits is attempting to resume on the ASP.NET context, but the request thread (using that ASP.NET context) is blocked waiting for the task to complete. This is what causes your deadlock.
I did try using .ConfigureAwait(false) on that first async method but without any success.
In order to avoid this deadlock using ConfigureAwait(false), it must be applied to every await in every method called. So DoSomethingAsync must use it for every await, every method that DoSomethingAsync calls must use it for every await (e.g., Authenticate), every method that those methods call must use it for every await (e.g., PostAsync), etc. Note that at the end here, you are dependent on library code, and in fact HttpClient has missed a few of these in the past.
I've found that I can make the first version work if I change it [to use Task.Run].
Yup. Task.Run will execute its delegate on a thread pool thread without any context. So this is why there's no deadlock: none of the awaits attempt to resume on the ASP.NET context.
Why don't you use it the correct way. async void btn_Click and await Class1.DoSomethingAsync() ?
Do not use Task.Run with already asynchronous methods, this is waste of threads. Just change signature of button_click eventhandler
And here's the right answer: don't block on asynchronous code. Just use an async void event handler and use await instead.
P.S. ASP.NET Core no longer has an ASP.NET context, so you can block as much as you want without fear of deadlocks. But you still shouldn't, of course, because it's inefficient.
Recently I've finished designing a PagedDataGridView custom control, it works very well, but now I'd like to improve its performance.
How?, well, I've seen some UI blocking when showing new pages.
In short words, this:
public class PagedDataGridView() : UserControl
{
Paging paging = new Paging();
public PagedDataGridView()
{
paging.LoadDataClientMethod = LoadDataOnGrid;
}
private void LoadDataOnGrid()
{
// Some heavy set data source here, using functions from 'paging' object
}
}
What I'm trying to do (using the async / await pattern):
That async method DoPaging pauses until the await 'LoadDataOnGrid' is complete, that way the UI thread is not blocked, be asynchronous.
// Class that handles paging methods, variables,
// also offers navigation controls, such as buttons, labels, etc.
internal class Paging
{
// Represents the method that code client uses to load its own data
public Action LoadDataClientMethod;
// HERE:
private async Task DoPaging()
{
// some calculations
if (LoadDataClientMethod != null)
{
// I realizad that calling Run method, runs it out of context
// How to run this method, and update the UI
await Task.Run(() => LoadDataClientMethod());
}
// Update controls, showing current page, etc
UpdateUI();
}
// Navigation buttons
private void btnGoNextPage(object sender, EventArgs e)
{
// go next page calculations
// Then how to call the async method
DoPaging(); // -> doing this, VS shows a warning:
/* Because this call is not awaited, the current method
continues to run before the call is completed */
}
}
I'm just starting to learn about async - await coding, any correction or advice will be greatly appreciated, thanks.
There is a big difference between:
private void btnGoNextPage(object sender, EventArgs e)
{
DoPaging();
}
and
private async void btnGoNextPage(object sender, EventArgs e)
{
await DoPaging();
}
Exception handling. If the former throws an exception, two things might happen:
If you're using .NET 4.0, the swallowed task will be re-thrown from the Finalizer thread and will cause your application to crash
If you're using .NET 4.5, the task will be swallowed and will go un-noticed and will not be re-thrown at all, thus possibly entering your application in a corrupted state which you wont be aware of.
in the latter example, the exception will propogate to the await point, and you can handle it gracefully by adding a try-catch block.
As a side note, i asked you in the comments what kind of work is being done that is blocking your UI thread, and you said that you are making a call to your database to retrieve data.
Work being done against a database is IO bound work, and most providers expose async endpoints to access data, such as Entity Framework, ADO.NET, etc. You can make use of that naturally async behavior by not using any threadpool threads to do the work for you (with Task.Run as you're doing in your example). You can do that when you go "async all the way", and your database query can be used with the await keyword. That way, while the query is retrieving the data, the thread that invoked the query (in your example, the UI thread) is freed and can do more work, thus your UI will stay responsive.
I suggest you look into that and see if your database provider has those async endpoints.
Just add async to the button click event handler method and await the call to DoPaging():
private async void btnGoNextPage(object sender, EventArgs e)
{
await DoPaging();
}
The difference between doing it like this rather than the way you had that gives the warning (and is in fact why the warning is given) is that if you added any code after the call to DoPaging() in the event handler it would now occur after the task has complete, whereas before it would execute immediately after the call.
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.
Firstly a forward apology: I cannot isolate the following bug into a simple console application. However, in my relatively simple ASP.NET Web Forms application, the following code will cause the current thread to block indefinitely:
public class MyModule : IHttpModule
{
public void Dispose()
{
}
public void Init(System.Web.HttpApplication context)
{
context.BeginRequest += this.Context_BeginRequest;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
Sleep().Wait();
var x = 2; // This line is never hit.
}
private async Task Sleep()
{
await TaskEx.Run(() => System.Threading.Thread.Sleep(1000));
}
}
The task state just remains as 'WaitingForActivation'. Does anyone know why this would happen?
EDIT: The comment from Stephen Cleary sheds more light:
AspNetSynchronizationContext is the strangest implementation. It treats Post as synchronous rather than asynchronous and uses a lock to execute its delegates one at a time. AspNetSynchronizationContext does not require marshaling back to the same thread (but does need to take the lock); the deadlock on Wait is because the continuation is waiting for the lock (held by the thread in the event handler)
My guess is that there's a SynchronizationContext which is forcing the continuation to run on the same thread as the event handler. Your event handler is blocking that thread, so the continuation never runs, which means the event handler will never unblock.
That's only a guess though - it's the only thing I can think of which makes sense at the moment.
One option to try to unblock this is to change your Sleep method to:
private async Task Sleep()
{
await TaskEx.Run(() => System.Threading.Thread.Sleep(1000))
.ConfigureAwait(continueOnCapturedContext: false);
}
That will allow the continuation to complete on a different context.
I'm surprised there is such a synchronization context, mind you... I'd expect all of this to just happen on the thread pool. Possibly BeginRequest is treated slightly specially.