0

I've got an ObservableCollection<string> binded to a WPF view. There is a method that refreshs the content of the collection. This method is processing in a background thread and takes a few seconds. To avoid errors, the called method is synchronized.

It may happen, that the method is called, even if the previous processing has not finished. In that case, I would like to stop the current processing and start the new one.

private ObservableCollection<string> col = new ObservableCollection<string>();

private async void Refresh()
{
    var ui = TaskScheduler.FromCurrentSynchronizationContext();
    await Task.Factory.StartNew(() =>
        {
            return GetNewObjects();
        }).ContinueWith(t =>
        {
            col = t.Result;
        }, ui); 
}

[MethodImpl(MethodImplOptions.Synchronized)]
private ObservableCollection<string> GetNewObjects() 
{
    // processing
}

My idea is to save a reference of the corresponding Task in Refresh() and check in every call, if there is already a running task. Is that the right (and safe) way to stop a task?

Joehl
  • 3,671
  • 3
  • 25
  • 53
theawak3r
  • 7
  • 4
  • 4
    You'd probably try a `CancellationToken`. By the way, why not `Task.Run()`? Are you stuck with .NET 4.0? – dymanoid May 08 '17 at 07:10
  • To be quite honest, no.. Im new to .net at all. I was looking for a nice way to refresh the view, without freeze the ui thread. This was the first working way to me. Why use Task.Run()? What are the benefits? – theawak3r May 08 '17 at 07:16
  • I don't think that is possible. The continuation on the UI thread is a killer detail and is always going to cause deadlock. Not the only problem, GetNewObjects() must be using *some* kind of data from the UI to produce a different result. We can't see it, but the only thing that makes sense. You cannot allow that data to change while it is executing. – Hans Passant May 08 '17 at 08:06
  • GetNewObjects() will be called every time a checkbox in another ObservableCollection is clicked. Every checked Checkbox will be the input for the GetNewObjects() Method. Actually the '[MethodImpl(MethodImplOptions.Synchronized)]' causes an sequentially processing. But that’s the problem. When the method is called 2 times, the interesting result is the last call. After every call the previous result is no more relevant. – theawak3r May 08 '17 at 08:20
  • `Task.Run()` is the preferred method to start a task in .NET 4.5+, read [this answer](http://stackoverflow.com/a/29693430/2846483) for details. – dymanoid May 08 '17 at 14:32

1 Answers1

0

You should follow the async/await path all way along.

To cancel a task use the CancellationTokenSource and its CancellationToken

private ObservableCollection<string> col = new ObservableCollection<string>();

private CancellationTokenSource _refreshCancellation;

private async void Refresh()
{
    // cancel the last Refresh action
    if ( _refreshCancellation != null )
    {
        _refreshCancellation.Cancel();
    }

    _refreshCancellation = new CancellationTokenSource();
    var token = _refreshCancellation.Token;

    try
    {
        var newObjects = await GetNewObjectsAsync( token );
        col = new ObservableCollection<string>( newObjects );
    }
    catch ( OperationCanceledException )
    {
    }

}

private async Task<ICollection<string>> GetNewObjectsAsync( CancellationToken cancellationToken )
{
    for ( int i = 0; i < 5; i++ )
    {
        await Task.Delay( 100 ).ConfigureAwait( false );
        // check if we had to cancel, but only where it is safe to cancel
        cancellationToken.ThrowIfCancellationRequested();
    }
    return new List<string> { "a", "b", "c", };
}
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Thanks for this advice. My background task is a SQL query and not a loop. So I need to kill this SQL operation. Is this possible at all by a CancellationToken? I got this new problem after reading the posts here and the functionality of these tokens. – theawak3r May 08 '17 at 11:41
  • 1
    If you have a new problem then you should ask a new question if you cannot find any solution. Thats how SO works – Sir Rufo May 08 '17 at 12:01
  • Here is one on SO dealing with cancelling a SQL query http://stackoverflow.com/questions/24738417/canceling-sql-server-query-with-cancellationtoken – Sir Rufo May 08 '17 at 12:04
  • Thanks four you help.. You're right. I got a new problem. I will try to solve this. Otherwise I will ask a new question. Nevertheless, your answer solves the problem of this question. – theawak3r May 09 '17 at 04:56