I have a WPF application where I press a button and the following code executes:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
new Thread(
async () =>
{
Action lambda = async () =>
{
await Task.Delay(5000);
MessageBox.Show("Lambda done");
};
await this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, lambda).Task;
MessageBox.Show("Dispatch done");
}).Start();
}
MSDN documentation of DispatcherOperation.Task says : Gets a Task<TResult> that represents the current operation. (It's not a Task<TResult> tho :/)
So I thought awaiting this would mean that the MessageBox.Show("Dispatch done"); will only be executed after the lambda is finished. However, it is not the case. The MessageBox with "Dispatch done" text is shown as soon as the button is pressed, and the one with "Lambda done" text is shown 5 seconds after, as expected.
Can someone explicate this? I don't understand why it is happening.
This line:
Action lambda = async ...
is creating an async void lambda rather than an async Task lambda. One of the (many) problems with async void is that there is no (easy) way to tell when it completes. For this reason, "avoid async void" is one of the best practices in my MSDN article on the subject.
I haven't tried it, but I suspect that Dispatcher.BeginInvoke can only take Action delegates (and not the more async-friendly Func<Task>), in which case it may be better to use Dispatcher.Invoke and pass a Func<Task>. In my own code, though, I avoid Dispatcher entirely; I find it encourages worse code. Instead of Dispatcher, consider using await and/or Progress<T>.
Related
For PeriodicTimer (AsyncTimer at the time), regarding WaitForNextTickAsync, David Fowler mentioned "The execution context isn't captured" (here) via (here). However, given that was not necessarily the final implementation, I reviewed the PeriodicTimer documentation which makes no mention of context capturing.
Based on Stephen Toub's decade old, but still excellent, "The Task-based Asynchronous Pattern," and the following code:
private CancellationTokenSource tokenSource;
private async void start_Click(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource();
var second = TimeSpan.FromSeconds(1);
using var timer = new PeriodicTimer(second);
try
{
while (await timer.WaitForNextTickAsync(tokenSource.Token).ConfigureAwait(false))
{
if (txtMessages.InvokeRequired)
{
txtMessages.Invoke(() => txtMessages.AppendText("Invoke Required..." + Environment.NewLine));
}
else
{
txtMessages.AppendText("Invoke NOT Required!" + Environment.NewLine);
}
}
} catch (OperationCanceledException)
{
//disregard the cancellation
}
}
private void stop_Click(object sender, EventArgs e)
{
tokenSource.Cancel();
}
If ConfigureAwait is passed true (or removed entirely), my output is as follows:
Invoke NOT Required!
Invoke NOT Required!
Invoke NOT Required!
Invoke NOT Required!
...
However, if ConfigureAwait is passed false, my output is as follows:
Invoke Required...
Invoke Required...
Invoke Required...
Invoke Required...
...
Unless I'm confusing SynchronizationContext with "executing thread," it seems like the current SynchronizationContext IS captured by default. Can anyone (maybe one of the greats) please clarify?
The new (.NET 6) PeriodicTimer component is not like all other Timer components that raise events or execute callbacks. This one resembles more the Task.Delay method. It exposes a single asynchronous method, the method WaitForNextTickAsync. This method is not special in any way. It returns a standard ValueTask<bool>, not some kind of exotic awaitable like the Task.Yield method (YieldAwaitable).
When you await this task, you control the SynchronizationContext-capturing behavior of the await like you do for any other Task or ValueTask: with the ConfigureAwait method. If you know how to use the ConfigureAwait with the Task.Delay, you also know how to use it with the PeriodicTimer.WaitForNextTickAsync. There is absolutely no difference.
If you don't know what the ConfigureAwait does, or you want to refresh your memory, there is a plethora of good articles to read. For the sake of variance I'll suggest this old 5-minute video by Lucian Wischik: Tip 6: Async library methods should consider using Task.ConfigureAwait(false)
I am creating a simple wpf desktop application. UI have just a button and code in .cs file like.
private void Button_Click_2(object sender, RoutedEventArgs e)
{
FunctionA();
}
public void FunctionA()
{
Task.Delay(5000).Start();
MessageBox.Show("Waiting Complete");
}
But surprisingly line Task.Delay(5000).Start(); is throwing an InvalidOperationException:
Start may not be called on a promise-style task.
Can any one help why it is like this?
You are getting that error because the Task class already started the task before giving it to you. You should only ever call Start on a task that you create by calling its constructor, and you shouldn't even do that unless you have a compelling reason to not start the task when you create it; if you want it started right away you should use Task.Run or Task.Factory.StartNew to both create and start a new Task.
So, now we know to just get rid of that pesky Start. You'll run your code and find that the message box is shown right away, not 5 seconds later, what's up with that?
Well, Task.Delay just gives you a task that will be completed in 5 seconds. It doesn't stop execution of the thread for 5 seconds. What you want to do is have some code that's executed after that task finishes. That's what ContinueWith is for. It lets you run some code after a given task is done:
public void FunctionA()
{
Task.Delay(5000)
.ContinueWith(t =>
{
MessageBox.Show("Waiting Complete");
});
}
This will behave as expected.
We could also leverage C# 5.0's await keyword to add continuations more easily:
public async Task FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}
While a full explanation of what's going on here is beyond the scope of this question, the end result is a method that behaves very similar to the previous method; it will show a message box 5 seconds after you call the method, but the method itself will return [almost] right away in both cases. That said, await is very powerful, and allows us to write methods that seem simple and straightforward, but that would be much harder and messier to write using ContinueWith directly. It also greatly simplifies dealing with error handling, taking out a lot of boilerplate code.
How-to: Call await method from a delegate void event method?
Converting (an ASP.NET) app to async is an all in - granted. But what about the places where it is not supported?
var policy = new CacheItemPolicy();
policy.UpdateCallback = CacheEntryUpdateCallback;
void VoidDelegate(CacheEntryUpdateArguments arguments) {
// need this - but no can't do :(
await SomeApi.AsyncOnlyMethodAsync();
}
Await is not allow there, so what to do? What is the right way of doing this? The question also, applies to event handlers on WebForms like myButton.Commmand += new CommandEventHandler(VoidDelegate)
Is it A:
void VoidDelegate(CacheEntryUpdateArguments arguments) {
Task.Factory.StartNew(new Func<Task>(async () => {
await SomeApi.AsyncOnlyMethodAsync();
})).Unwrap().Wait();
}
This will work but spin up another thread? At least it will not make the thread avail for the pool and the purpose of async here is gone.
Or is this supported B:
async void VoidDelegate(CacheEntryUpdateArguments arguments) {
await SomeApi.AsyncOnlyMethodAsync();
}
But this is a fire-and-forget - but cache update event needs to fetch the new data before returning result. Same goes for e.g. button command event - page could be completed before the command has finished.
You can mark any method that returns void, Task, or Task<T> as async. Marking a method as async doesn't change the signature of the method, so you're allowed to simply mark VoidDelegate as async if you want it to await things.
Note that, since you now have an async void method, this will be a fire-and-forget method. When someone invokes the delegate the method will return very quickly, while the asynchronous work continues to happen, and the caller will have no way of knowing when the work finishes, or if it errors.
Given the following code, why does ask.WhenAny never return when provided with a Task.Delay of 1 second? Technically I'm not sure if it does return after a extended amount of time, but it doesn't after 15 seconds or so after which I manually kill the process. According to the documentation I shouldn't be required to manually start the delayTask, and in fact I receive a exception if I try to do so manually.
The code is being called from the UI thread when a user selects a context menu item in a WPF application, although it works fine if I have the click method specified for the context menu item run this code in a new thread.
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
SomeMethod();
...
}
public void SomeMethod()
{
...
SomeOtherMethod();
....
}
public void SomeOtherMethod()
{
...
TcpClient client = Connect().Result;
...
}
//In case you're wondering about the override below, these methods are in
//different classes i've just simplified things here a bit so I'm not posting
//pages worth of code.
public override async Task<TcpClient> Connect()
{
...
Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port);
Task delayTask = Task.Delay(1000);
if (await Task.WhenAny(connectTask, delayTask) == connectTask)
{
Console.Write("Connected\n");
...
return tcpClient;
}
Console.Write("Timed out\n");
...
return null;
}
If I change ContextMenuItem_Click to the following it works fine
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
new Thread(() => SomeMethod()).Start();
...
}
I predict that further up your call stack, you're calling Task.Wait or Task<T>.Result. This will cause a deadlock that I explain in full on my blog.
In short, what happens is that await will (by default) capture the current "context" and use that to resume its async method. In this example, the "context" is the WPF UI context.
So, when your code does its await on the task returned by WhenAll, it captures the WPF UI context. Later, when that task completes, it will attempt to resume on the UI thread. However, if the UI thread is blocked (i.e., in a call to Wait or Result), then the async method cannot continue running and will never complete the task it returned.
The proper solution is to use await instead of Wait or Result. This means your calling code will need to be async, and it will propagate through your code base. Eventually, you'll need to decide how to make your UI asynchronous, which is an art in itself. At least to start with, you'll need an async void event handler or some kind of an asynchronous MVVM command (I explore async MVVM commands in an MSDN article). From there you'll need to design a proper asynchronous UI; i.e., how your UI looks and what actions it permits when asynchronous operations are in progress.
I am creating a simple wpf desktop application. UI have just a button and code in .cs file like.
private void Button_Click_2(object sender, RoutedEventArgs e)
{
FunctionA();
}
public void FunctionA()
{
Task.Delay(5000).Start();
MessageBox.Show("Waiting Complete");
}
But surprisingly line Task.Delay(5000).Start(); is throwing an InvalidOperationException:
Start may not be called on a promise-style task.
Can any one help why it is like this?
You are getting that error because the Task class already started the task before giving it to you. You should only ever call Start on a task that you create by calling its constructor, and you shouldn't even do that unless you have a compelling reason to not start the task when you create it; if you want it started right away you should use Task.Run or Task.Factory.StartNew to both create and start a new Task.
So, now we know to just get rid of that pesky Start. You'll run your code and find that the message box is shown right away, not 5 seconds later, what's up with that?
Well, Task.Delay just gives you a task that will be completed in 5 seconds. It doesn't stop execution of the thread for 5 seconds. What you want to do is have some code that's executed after that task finishes. That's what ContinueWith is for. It lets you run some code after a given task is done:
public void FunctionA()
{
Task.Delay(5000)
.ContinueWith(t =>
{
MessageBox.Show("Waiting Complete");
});
}
This will behave as expected.
We could also leverage C# 5.0's await keyword to add continuations more easily:
public async Task FunctionA()
{
await Task.Delay(5000);
MessageBox.Show("Waiting Complete");
}
While a full explanation of what's going on here is beyond the scope of this question, the end result is a method that behaves very similar to the previous method; it will show a message box 5 seconds after you call the method, but the method itself will return [almost] right away in both cases. That said, await is very powerful, and allows us to write methods that seem simple and straightforward, but that would be much harder and messier to write using ContinueWith directly. It also greatly simplifies dealing with error handling, taking out a lot of boilerplate code.