【问题标题】:uwp C# cancel async task and re-runuwp C#取消异步任务并重新运行
【发布时间】:2017-11-10 21:29:19
【问题描述】:

提前致谢。 我想使用 foreach 将项目添加到 `GridView 。这是在 async 方法中完成的。 我想从其他 async 方法中检索项目并将它们显示在 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);
    }
}

为方便起见,内容被简化了。

当我在按钮单击处理程序中运行此方法时:

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"; }
}

问题来了:

如果我单击此按钮两次(快速单击): - 第一次点击,事件回调将被调用,我的任务将开始运行 - GridView 中会显示某些内容,但不会完成(以 80% 结束) - 第二次点击时,GridView 按预期清除,并加载了一些新内容,但GriView 仅显示第一次点击任务的最后 20%

那么,为什么第二次点击没有取消第一个任务呢?

我在网上搜索了很长时间,坚果没有找到任何东西。请帮助并尝试给出一些想法如何实现这一点。

我英语不好,谢谢

【问题讨论】:

  • 仅凭您分享的内容很难猜测发生了什么。乍一看,您的代码似乎是正确的。我唯一担心的是您在SetGridItems(ct.Token, /* and some items */) 之后进行的.AsAsyncAction().AsTask(cts.Token); 呼叫。如果您已经向该方法提供了取消令牌,则无需通过AsTask() 再次提供它。这同样适用于您的getBmethodAsync()getAmethodAsync(),您可以直接提供取消令牌并使用它来做您需要的事情。我的猜测是外部取消会产生这种副作用。
  • 感谢文森特和 CKII 的帮助,ct.ThrowIfCancellationRequested();工作了

标签: c# uwp async-await


【解决方案1】:

我在这里看到两个问题:

首先正如文森特在评论中所说,您在按钮单击处理程序中使用 AsAsyncAction().AsTask(cts.Token); 和方法本身中的 .AsTask(ct); 以有点多余的方式传递取消令牌。

其次,更重要的是,您将取消令牌传递给任务,但您从未在方法中使用它。将取消令牌传递给任务主要是并行使用,而不是异步工作。这是您协调和查询同时运行的多个任务的执行状态的一种方式。无论如何,它总是依赖于令牌本身在执行代码中的使用。这样想,您是在告诉任务对象本身您正在取消操作,但您的代码不知道如何处理它。

在异步开发中,您实际上不需要将取消传递给任务对象,因为您不必协调其中许多的状态,您只需执行一个。您应该将令牌传递给您的方法,并让您的代码处理它。
因此,在您的 SetGridItems 方法中,尝试执行以下操作:

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);
    }
}

确保在 GetXMethodAsync 方法中执行相同的操作。这样您的代码就知道如何处理取消。因为现在任务对象可能被取消了,但它仍然继续执行代码,因为它不知道在哪里停止。

有关取消任务的更多信息,您可以查看以下链接:
1.https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
2.https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/how-to-cancel-a-task-and-its-children

希望对你有帮助

【讨论】:

  • 非常感谢,成功了!!! :),谢谢,代码不知道如何处理,那就是问题所在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多