haihan jin
haihan jin

Reputation: 125

uwp C# cancel async task and re-run

Thanks in advance. I want to use a foreach to add items to a `GridView . This is done in an async method. I want to retrieve the items from other async methods and display them in a GridView:

public async Task SetGridItems(CancellationToken ct, /* and some items */)
{

    GridItems.clear();
    //get two list of item I want
    var ListA = await getBmethodAsync().AsTask(ct);
    var ListB = await getAmethodAsync().AsTask(ct);

    foreach (itemA A in ListA)
    {     
        GridItem i = new GridItem();

        //set name
        i.name = A.Name;

        //get icon
        i.image = img;

        GridItems.Add(i);
    }

    foreach (ItemB b in ListB)
    {
         GridItem i = new GridItem();
         i.name = b.Name;
         i.image.SetSource(icon);

         GridItems.Add(i);
    }
}

The content is simplified for convenience.

When I run this method in a button click handler:

private async void check_btn2_Click(object sender, RoutedEventArgs e)
{    
    if (cts != null) { 
        cts.Cancel();
        debug.Text = "cancel a job 4";
    }

    cts = new CancellationTokenSource();
    try
    {
       await SetGridItems(ct.Token, /* and some items */).AsAsyncAction().AsTask(cts.Token);
    }
    catch (OperationCanceledException) { debug.Text = "cancel a job"; }
}

Here is the problem:

If I click this button twice (clicking fast): - on the first click, the event callback will be called and my task will start to run - something will show in the GridView but will not complete (it ends at 80%) - on second click, the GridView is cleared as expected, and some new content is loaded but the GriViewis only showing the last 20% of the first click task

So, why does the second click not cancelling the first task ?

I've searched for a long time on net, nut haven't found anything. Please help and try to give some ideas how to achieve this.

I am not good at English and thanks

Upvotes: 0

Views: 1357

Answers (1)

CKII
CKII

Reputation: 1486

I see two problems here:

First as Vincent said in the comment, you're passing the cancellation token in a little redundant way with the AsAsyncAction().AsTask(cts.Token); in the button click handler and the .AsTask(ct); in the method itself.

Second, and much more important, you're passing the cancellation token to the task, but you're never using it in the method. Passing a cancellation token to a task is mostly used in parallel, not async work. It's a way for you to coordinate and query the execution state of multiple tasks running at once. And anyway, its always dependent on the usage of the token itself inside the executing code. Think of it this way, you're telling the task object itself that you're cancelling the operation, but your code doesn't know how to handle it.

In async development, you don't really need to pass the cancellation to the task object, since you don't have to coordinate the state of many of them, you're only executing the one. You should pass the token to your method, and let your code handle it.
So inside your SetGridItems method, try doing something like this:

public async Task SetGridItems(CancellationToken ct, /* and some items */)
{

    GridItems.clear();
    //get two list of item I want
    var ListA = await getBmethodAsync().AsTask(ct);
    var ListB = await getAmethodAsync().AsTask(ct);

    foreach (itemA A in ListA)
    {
        ct.ThrowIfCancellationRequested();

        GridItem i = new GridItem();

        //set name
        i.name = A.Name;

        //get icon
        i.image = img;

        GridItems.Add(i);
    }

    foreach (ItemB b in ListB)
    {
        ct.ThrowIfCancellationRequested();

        GridItem i = new GridItem();
        i.name = b.Name;
        i.image.SetSource(icon);

        GridItems.Add(i);
    }
}

Make sure to do the same in the GetXMethodAsync methods. This way your code knows how to handle the cancellation. Because right now, the task object may be cancelled, but it still keeps executing the code, since it doesn't know where to stop.

For more on task cancellation you can see the following links:
1. https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
2. https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-cancel-a-task-and-its-children

Hope this helps

Upvotes: 2

Related Questions