Best practice for consistently cancelling Async CancellationTokenSource - c#

So I have a combobox on my UI that on SelectionChanged it asynchronously goes off to a web service to pull back some information to be displayed on the UI (using the new C#5 async/await keywords). What I'm looking to do is cancel the current async request before sending a new one; for example if the user uses the keyboard to quickly cycle through all of the combobox items, the SelectionChanged event could fire multiple times (generating multiple async requests) before even the first async request returns.
So my async function that gets called from the combobox's SelectionChanged event looks like this:
public async Task<Connections> ConnectionsAsync()
{
return await Task.Factory.StartNew(() => Connections, _cancellationTokenSource.Token);
}
Where Connections is a property that goes off and hits the web service. So because the CancellationTokenSource cannot be reused once cancelled, I'm thinking of doing this:
public async Task<Connections> ConnectionsAsync()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
return await Task.Factory.StartNew(() => Connections, _cancellationTokenSource.Token);
}
The problem though is that sometimes I will be calling Cancel() when there is no async command running (e.g. the first time this function is called); so if I hookup any cancellation event handlers they will get called, even before I have made an async request.
Is there anyway to check if an async request is already running? Other than me doing something like:
public async Task<Connections> ConnectionsAsync()
{
if (_runningAsyncCommand)
_cancellationTokenSource.Cancel();
_cancellationTokenSource = new CancellationTokenSource();
_runningAsyncCommand = true;
return await Task.Factory.StartNew(() => Connections, _cancellationTokenSource.Token);
_runningAsyncCommand = false;
}
I have a few async functions that all use the same CancellationTokenSource, so I would have to implement this "plumbing" in all of those functions. Is this the best approach? Or is there a better way?
Also, if I expose _cancellationTokenSource publically so that other classes can register cancellation delegates with it, what is the best way to "transfer" these delegates over to the new CancellationTokenSource, since I'm creating a new one every time?
Thanks in advance!

Looks like a match made in heaven for Reactive Extensions. Define a throttle time that has to elapse (let's say 300mS) when observing the events from the Combobox before creating the Task
Code snippet subscribing to TextBox change events, but you'll get the idea:
var input (from evt in Observable.FromEvent<EventArgs>(txt, "TextChanged")
select ((TextBox)evt.Sender).Text)
.Throttle(TimeSpan.FromSeconds(1))
.DistinctUntilChanged();
using (input.Subscribe(inp => Console.WriteLine("You wrote: " + inp)))
{
// Do stuff
}

Related

Creating an async resource watcher in c# (service broker queue resource)

Partly as an exercise in exploring async, I though I'd try creating a ServiceBrokerWatcher class. The idea is much the same as a FileSystemWatcher - watch a resource and raise an event when something happens. I was hoping to do this with async rather than actually creating a thread, because the nature of the beast means that most of the time it is just waiting on a SQL waitfor (receive ...) statement. This seemed like an ideal use of async.
I have written code which "works", in that when I send a message through broker, the class notices it and fires off the appropriate event. I thought this was super neat.
But I suspect I have gotten something fundamentally wrong somewhere in my understanding of what is going on, because when I try to stop the watcher it doesn't behave as I expect.
First a brief overview of the components, and then the actual code:
I have a stored procedure which issues a waitfor (receive...) and returns a result set to the client when a message is received.
There is a Dictionary<string, EventHandler> which maps message type names (in the result set) to the appropriate event handler. For simplicity I only have the one message type in the example.
The watcher class has an async method which loops "forever" (until cancellation is requested), which contains the execution of the procedure and the raising of the events.
So, what's the problem? Well, I tried hosting my class in a simple winforms application, and when I hit a button to call the StopListening() method (see below), execution isn't cancelled right away as I thought it would be. The line listener?.Wait(10000) will in fact wait for 10 seconds (or however long I set the timeout). If I watch what happens with SQL profiler I can see that the attention event is being sent "straight away", but still the function does not exit.
I have added comments to the code starting with "!" where I suspect I have misunderstood something.
So, main question: Why isn't my ListenAsync method "honoring" my cancellation request?
Additionally, am I right in thinking that this program is (most of the time) consuming only one thread? Have I done anything dangerous?
Code follows, I tried to cut it down as much as I could:
// class members //////////////////////
private readonly SqlConnection sqlConnection;
private CancellationTokenSource cts;
private readonly CancellationToken ct;
private Task listener;
private readonly Dictionary<string, EventHandler> map;
public void StartListening()
{
if (listener == null)
{
cts = new CancellationTokenSource();
ct = cts.Token;
// !I suspect assigning the result of the method to a Task is wrong somehow...
listener = ListenAsync(ct);
}
}
public void StopListening()
{
try
{
cts.Cancel();
listener?.Wait(10000); // !waits the whole 10 seconds for some reason
} catch (Exception) {
// trap the exception sql will raise when execution is cancelled
} finally
{
listener = null;
}
}
private async Task ListenAsync(CancellationToken ct)
{
using (SqlCommand cmd = new SqlCommand("events.dequeue_target", sqlConnection))
using (CancellationTokenRegistration ctr = ct.Register(cmd.Cancel)) // !necessary?
{
cmd.CommandTimeout = 0;
while (!ct.IsCancellationRequested)
{
var events = new List<string>();
using (var rdr = await cmd.ExecuteReaderAsync(ct))
{
while (rdr.Read())
{
events.Add(rdr.GetString(rdr.GetOrdinal("message_type_name")));
}
}
foreach (var handler in events.Join(map, e => e, m => m.Key, (e, m) => m.Value))
{
if (handler != null && !ct.IsCancellationRequested)
{
handler(this, null);
}
}
}
}
}
You don't show how you've bound it to the WinForms app, but if you are using regular void button1click methods, you may be running into this issue.
So your code will run fine in a console app (it does when I try it) but deadlock when called via the UI thread.
I'd suggest changing your controller class to expose async start and stop methods, and call them via e.g.:
private async void btStart_Click(object sender, EventArgs e)
{
await controller.StartListeningAsync();
}
private async void btStop_Click(object sender, EventArgs e)
{
await controller.StopListeningAsync();
}
Peter had the right answer. I was confused for several minutes about what was deadlocking, but then I had my forehead slapping moment. It is the continuation of ListenAsync after the ExecuteReaderAsync is cancelled, because it's just a task, not a thread of its own. That was, after all, the whole point!
Then I wondered... OK, what if I tell the async part of ListenAsync() that it doesn't need the UI thread. I will call ExecuteReaderAsync(ct) with .ConfigureAwait(false)! Aha! Now the class methods don't have to be async anymore, because in StopListening() I can just listener.Wait(10000), the wait will continue the task internally on a different thread, and the consumer is none the wiser. Oh boy, so clever.
But no, I can't do that. Not in a webforms application at least. If I do that then the textbox is not updated. And the reason for that seems clear enough: the guts of ListenAsync invoke an event handler, and that event handler is a function which wants to update text in a textbox - which no doubt has to happen on the UI thread. So it doesn't deadlock, but it also can't update the UI. If I set a breakpoint in the handler which wants to update the UI the line of code is hit, but the UI can't be changed.
So in the end it seems the only solution in this case is indeed to "go async all the way down". Or in this case, up!
I was hoping that I didn't have to do that. The fact that the internals of my Watcher are using async methodologies rather than just spawning a thread is, in my mind, an "implementation detail" that the caller shouldn't have to care about. But a FileSystemWatcher has exactly the same issue (the need to control.Invoke if you want to update a GUI based on a watcher event), so that's not so bad. If I was a consumer that had to choose between using async or using Invoke, I'd choose async!

Cancel Async operation

private async void TriggerWeekChanged(Week currentWeek)
{
await LoadDataForSelectedWeek(currentWeek); //Split into multiple methods
}
In case a user hammers on the Change_Week Button how can I cancel the current Task, and start a new one with the new paramerters ?
I tried like this:
private async Task Refresh(Week selectedWeek, CancellationToken token)
{
Collection.Clear();
await LoadDataFromDatabase();
token.ThrowIfCancellationRequested();
await ApplyDataToBindings();
token.ThrowIfCancellationRequested();
//Do some other stuff
}
Problem is:
In my Collection I got data from multiple weeks when I hit the button to fast in a row.
Everything that is awaited will need to have visibility on that CancellationToken. If it is a custom Task that you wrote, it should accept it as an argument, and periodically within the function, check to see if it has been cancelled. If so, it should take any actions needed (if any) to stop or rollback the operation in progress, then call ThrowIfCancellationRequested().
In your example code, you propbably want to pass token in to LoadDataFromDatabase and ApplyDataToBindings, as well as any children of those tasks.
There may be some more advanced situations where you don't want to pass the same CancellationToken in to child tasks, but you still want them to be cancellable. In those cases, you should create a new, internal to the Task, Cancellationtoken that you use for child tasks.
An important thing to remember is that ThrowIfCancellationRequested marks safe places within the Task that the Task can be stopped. There is no guaranteed safe way for the runtime to automatically detect safe places. If the Task were to automatically cancel its self as soon as cancellation was requested, it could potentially be left in an unknown state, so it is up to developers to mark those safe locations. It isn't uncommon to have several calls to check cancellation scattered throughout your Task.
I've just notice that your TriggerWeekChanged function is async void. This is usually considered an anti-pattern when it is used on something that is not an event handler. It can cause a lot of problems with tracking the completed status of the async operations within the method, and handling any exceptions that might be thrown from within it. You should be very weary of anything that is marked as async void that isn't an event handler, as it is the wrong thing to do 99%, or more, of the time. I would strongly recommend changing that to async Task, and consider passing in the CancellationToken from your other code.
You did not mention about how LoadDataForSelectedWeek and Refresh interact.
For short you need to create one CancellationTokenSource instance that handle every click. Then pass it to that method and do Cancel method every time new one appear.
private async void TriggerWeekChanged(Week currentWeek, CancellationTokenSource tokenSource)
{
tokenSource.Cancel();
try
{
var loadDataTask = Task.Run(() => LoadDataForSelectedWeek(currentWeek, tokenSource.Token), tokenSource.Token); //Split into multiple methods
}
catch(OperationCanceledException ex)
{
//Cancelled
}
}
LoadDataForSelectedWeek -> Refresh (?)
private async Task Refresh(Week selectedWeek, CancellationToken token)
{
Collection.Clear();
await LoadDataFromDatabase();
token.ThrowIfCancellationRequested();
await ApplyDataToBindings();
token.ThrowIfCancellationRequested();
//Do some other stuff
}

async/await on CacheItemPolicy.UpdateCallback event and other event delegates

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.

How does the async/await return callchain work?

I had a situation recently where I had an ASP.NET WebAPI controller that needed to perform two web requests to another REST service inside its action method. I had written my code to have functionality separated cleanly into separate methods, which looked a little like this example:
public class FooController : ApiController
{
public IHttpActionResult Post(string value)
{
var results = PerformWebRequests();
// Do something else here...
}
private IEnumerable<string> PerformWebRequests()
{
var result1 = PerformWebRequest("service1/api/foo");
var result = PerformWebRequest("service2/api/foo");
return new string[] { result1, result2 };
}
private string PerformWebRequest(string api)
{
using (HttpClient client = new HttpClient())
{
// Call other web API and return value here...
}
}
}
Because I was using HttpClient all web requests had to be async. I've never used async/await before so I started naively adding in the keywords. First I added the async keyword to the PerformWebRequest(string api) method but then the caller complained that the PerformWebRequests() method has to be async too in order to use await. So I made that async but now the caller of that method must be async too, and so on.
What I want to know is how far down the rabbit hole must everything be marked async to just work? Surely there would come a point where something has to run synchronously, in which case how is that handled safely? I've already read that calling Task.Result is a bad idea because it could cause deadlocks.
What I want to know is how far down the rabbit hole must everything be
marked async to just work? Surely there would come a point where
something has to run synchronously
No, there shouldn't be a point where anything runs synchronously, and that is what async is all about. The phrase "async all the way" actually means all the way up the call stack.
When you process a message asynchronously, you're letting your message loop process requests while your truly asynchronous method runs, because when you go deep down the rabit hole, There is no Thread.
For example, when you have an async button click event handler:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await DoWorkAsync();
// Do more stuff here
}
private Task DoWorkAsync()
{
return Task.Delay(2000); // Fake work.
}
When the button is clicked, runs synchronously until hitting the first await. Once hit, the method will yield control back to the caller, which means the button event handler will free the UI thread, which will free the message loop to process more requests in the meanwhile.
The same goes for your use of HttpClient. For example, when you have:
public async Task<IHttpActionResult> Post(string value)
{
var results = await PerformWebRequests();
// Do something else here...
}
private async Task<IEnumerable<string>> PerformWebRequests()
{
var result1 = await PerformWebRequestAsync("service1/api/foo");
var result = await PerformWebRequestAsync("service2/api/foo");
return new string[] { result1, result2 };
}
private async string PerformWebRequestAsync(string api)
{
using (HttpClient client = new HttpClient())
{
await client.GetAsync(api);
}
// More work..
}
See how the async keyword went up all the way to the main method processing the POST request. That way, while the async http request is handled by the network device driver, your thread returns to the ASP.NET ThreadPool and is free to process more requests in the meanwhile.
A Console Application is a special case, since when the Main method terminates, unless you spin a new foreground thread, the app will terminate. There, you have to make sure that if the only call is an async call, you'll have to explicitly use Task.Wait or Task.Result. But in that case the default SynchronizationContext is the ThreadPoolSynchronizationContext, where there isn't a chance to cause a deadlock.
To conclude, async methods shouldn't be processed synchronously at the top of the stack, unless there is an exotic use case (such as a Console App), they should flow asynchronously all the way allowing the thread to be freed when possible.
You need to "async all the way up" to the very top of the call stack, where you reach a message loop that can process all of the asynchronous requests.

If using a Timer control do you need to use Async Task?

I have a Windows form GUI that polls a database server every X minutes. If it finds data it posts the data to an HTTP API. Since I am using a Timer to accomplish the polling do I still need to make use of asynchronous code such as this when calling the API?
Task.Run(async () =>
{
// Call HTTP code here
}).Wait();
I would like the GUI to remain responsive and not lock up.
I am new to asynchronous development in .NET and still not sure how to implement it properly.
Since I am using a Timer to accomplish the polling do I still need to make use of asynchronous code such as this when calling the API?
If you're using System.Threading.Timer or System.Timers.Timer then you dont have to use an async api to keep your GUI responsive as these will invoke the delegate passed to them on a ThreadPool thread. Specifically in your example, you're mistakenly using Wait which waits synchronously on the new thread you're creating, which makes no sense.
If the only thing your delegate is doing is IO bound calls, such as a HTTP request or an async database call (for example, EF exposes async endpoints) then you dont need the extra thread to keep you UI responsive.
Here's an example using Task.Delay (which internally uses a timer) which is purely async:
public async Task PoolAndWaitAsync(int timeToWait)
{
var httpClient = new HttpClient();
while (true)
{
// Query database
var data = await MyDatabase.QueryAsync(..);
if (data != null)
{
var response = await httpClient.PostAsync(..);
}
await Task.Delay(timeToWait);
}
}
It depends on whether you make your HTTP Code asynchronous. Provided you use an async API within that event handler, it should be fine to not wrap in a separate Task.Run.
The Task object iteself performs in async operations. Servy is right. You have to remove the .Wait().
But as long as you want to populate some controls within your async method, you have to be sure to be thread safe.
Cheers, Martin

Categories

Resources