简体   繁体   中英

Best practice for consistently cancelling Async CancellationTokenSource

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 (eg 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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM