并行运行任务,同时保留取消任务的选项,并在一切完成后执行某些操作



我调用一个异步方法,而该方法又调用需要启动几个异步操作。

完成所有异步操作后,我需要通知调用方操作是否已完成或取消。代码如下所示。它似乎可以工作,直到它因FatalExecutionEngineError而崩溃。我不进行任何 P/调用或不安全的调用。

   public class Fetcher
    {
        private List<ItemsBoundToUI> uiItems;
        public async Task<bool> Fetch()
        {
            List<Task> currentlyRunningTasks = new List<Task>();

                var src = new CancellationTokenSource();

                foreach (var channel in this.uiItems)
                {
                    var task = FetchForItem(channel, src.Token);
                    currentlyRunningTasks.Add(task);
                }
            bool wasCancelled = await Task.Factory.ContinueWhenAll(currentlyRunningTasks.ToArray(),
                                                                    (s) =>
                                                                    {
                                                                        bool wasC = s.Any(ss => ss.IsCanceled);
                                                                        return wasC;
                                                                    }
                                                                    );

           return !wasCancelled;
        }
        private async Task FetchForItem(ItemsBoundToUI channel, CancellationToken src)
        {
            var subitems = await dataRepository.GetSubItemsAsync(channel.ID);
            if (src.IsCancellationRequested)
                return;
            channel.SubItems = subitems;
        }
    }

错误消息显示

运行时遇到致命错误。错误的地址 在0x6c108144,在线程0xd94上。错误代码为0xc0000005。这 错误可能是 CLR 中的错误,也可能是不安全或不可验证的错误 部分用户代码。此错误的常见来源包括用户 COM 互操作或 PInvoke 的封送处理错误,这可能会损坏 叠。

怎么可能等到所有任务都完成,或者等到它们被取消,然后返回一个值,指示它们是完成还是被取消?

FatalExecutionEngineError听起来像是CLR错误。请开发一个最小的重现,并通过Microsoft连接将其报告给Microsoft。

最好让取消按照设计的方式进行(通过例外(。要await多个任务,请使用 Task.WhenAll

public class Fetcher
{
  private List<ItemsBoundToUI> uiItems;
  public async Task<bool> Fetch()
  {
    List<Task> currentlyRunningTasks = new List<Task>();
    var src = new CancellationTokenSource();
    foreach (var channel in this.uiItems)
    {
      var task = FetchForItem(channel, src.Token);
      currentlyRunningTasks.Add(task);
    }
    try
    {
      await Task.WhenAll(currentlyRunningTasks);
      return true;
    }
    catch (OperationCanceledException)
    {
      return false;
    }
  }
  private async Task FetchForItem(ItemsBoundToUI channel, CancellationToken src)
  {
    var subitems = await dataRepository.GetSubItemsAsync(channel.ID);
    src.ThrowIfCancellationRequested();
    channel.SubItems = subitems;
  }
}

最新更新