5

I have the following task that is run when the section item of a list box changes.

I am trying to cancel any task that are running when a user changes their selection and start a new task . I can seem to figure out why the code is not working.

The code

CancellationTokenSource cts;
// The event handeler for when the user makes a selection in the list box 
private async void lb1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    clearfileds();

    if (cts != null)
       { cts.Cancel(); }

    cts = new CancellationTokenSource();

    var token = cts.Token;
    string sid = lb1.SelectedItem.ToString();

    try
    {   
        var series = await LoadSeriesAsync(token, Int32.Parse(sid));
        var poster = await LoadPosterAsync(series.PosterBanners[0]);

        UpdateSeries(series);
        if (File.Exists(poster))
        {
            ImageSource imageSource = new BitmapImage(new Uri(poster));
            imgPoster.Source = imageSource;
        }
    }
    catch (OperationCanceledException)
    {MessageBox.Show("we cancell some thing");}

    catch (FormatException)
    {MessageBox.Show("Please enter a valid series id");}

}


private async Task<TvdbSeries> LoadSeriesAsync(CancellationToken ct, int _seriesId)
    {  TvdbSeries seriesloaded = null;
       CancellationToken token = ct;

        Task<TvdbSeries> SeriesLoadTask = Task.Run(() =>
        {   
            m_tvdbHandler = new TvdbHandler(CacheProvider, "49E28C3EB13EB1CF");
            m_tvdbHandler.InitCache();
            token.ThrowIfCancellationRequested();

            try
            {   seriesloaded = m_tvdbHandler.GetSeries(_seriesId, TvdbLanguage.DefaultLanguage, true, true, true, true);
                //Just for the test 
                System.Threading.Thread.Sleep(9000);
            }

            catch (OperationCanceledException)
            { }

            catch (TvdbInvalidApiKeyException ex)
            { MessageBox.Show(ex.Message);}
            catch (TvdbNotAvailableException ex)
            { MessageBox.Show(ex.Message);}

            return seriesloaded;
        });

        try
        { seriesloaded = await SeriesLoadTask; }

        catch (OperationCanceledException)
                {}
        return seriesloaded;
    }


 private  async Task<string> LoadPosterAsync(object _param)
        {
            string posterpath ;
            Task<string> PosterLoad = Task.Run(() =>
            {

                TvdbPosterBanner banner = (TvdbPosterBanner)_param;
                banner.LoadBanner();
                posterpath = CacheFolder + @"\" + banner.SeriesId + @"\img_posters_" + (banner.BannerPath).Replace(@"posters/", "");
                return posterpath;
            });


            try
            { posterpath = await PosterLoad; }

            catch (OperationCanceledException)
                {   
                    posterpath = "";
                }
            return posterpath;
        }

So I am trying to get LoadSeriesAsync to cancel all other events that are running and then only run LoadPosterAsync if LoadSeriesAsync is allowed to finish (user does not change selection before it is loaded).

justinf
  • 1,246
  • 2
  • 19
  • 39
  • Can you be more specific about "not working"? – stuartd Oct 23 '13 at 15:50
  • Sorry ,The task does not get cancelled – justinf Oct 23 '13 at 16:18
  • You need to manually check `token.IsCancellationRequested` in LoadSeriesAsync - see http://stackoverflow.com/a/3713113/43846 – stuartd Oct 23 '13 at 17:12
  • Thread.Sleep(9000) is not a proper simulation of asynchronous code. It will freeze your UI. And don't check for cancellation *before* you run expensive code, check it *while* running it. – Hans Passant Nov 10 '13 at 16:44
  • @ Hans Passant Hi ,where in the code should i check if the cancelation token is being called ? would you mind editing my example to show me what you mean? – justinf Nov 11 '13 at 14:28

2 Answers2

3

I am trying to get LoadSeriesAsync to cancel all other events that are running and then only run LoadPosterAsync if LoadSeriesAsync is allowed to finish

So, just check the token before you call LoadPosterAsync:

var series = await LoadSeriesAsync(token, Int32.Parse(sid));
token.ThrowIfCancellationRequested();
var poster = await LoadPosterAsync(series.PosterBanners[0]);

On a side note, you should be passing token down the stack to any long-running operations, e.g., TvdbHandler.GetSeries.

Also, it's usually a bad idea to do catch (OperationCanceledException) { }; usually, the desired semantics are to allow the cancellation to propagate out.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I tried your suggestion but it does not seem the tasks are being cancelled . I put a sleep before the `seriesloaded = m_tvdbHandler.GetSeries` so that as I changed selection it would slow down the task when changing selection rapidly i see that there is more than one task and not canceled . This is what it looked like [screen of tasks](http://i.imgur.com/mRHpX42.jpg?1). – justinf Oct 23 '13 at 18:50
  • As I noted in my answer, you need to modify `GetSeries` so that it honors a cancellation token. – Stephen Cleary Oct 23 '13 at 20:40
  • Is there another way to do it because I cant change GetSeries because its functionality comes from a custom dll that some one else made , so i have no control over the code . – justinf Oct 24 '13 at 05:35
  • @justinf: There is no good way to cancel uncancelable code. The only real option is to load the uncancelable code into a separate process and kill it when you need to cancel. – Stephen Cleary Oct 24 '13 at 12:51
  • Would you mind updating you answer to show how to do it that way ? Thanks for your help its nice to know the correct way of doing thing . – justinf Oct 25 '13 at 05:14
-1

use Thread and at selection Check if Thread.IsAlive if equals to true Thread.Abort() and then Thread.Start(); easy

Agent_Spock
  • 1,107
  • 2
  • 16
  • 44