【问题标题】:Process a list of tasks asynchronously using Task.WhenAny使用 Task.WhenAny 异步处理任务列表
【发布时间】:2021-05-14 12:27:45
【问题描述】:

我创建了一个任务列表。一旦列表中的某个任务完成,我想处理它的结果(在这种情况下,打印一个 blob 的名称)。

代码(包含在异步函数中)如下所示:

List<Task<BlobResultSegment>> taskList = new List<Task<BlobResultSegment>>();
BlobContinuationToken token = null;
do
{
    taskList.Add(blobContainer.ListBlobsSegmentedAsync(token));
}
while (token != null);
            
while(taskList.Any())
{
    Task<BlobResultSegment> completedTask = await Task.WhenAny(taskList);
    await completedTask.ContinueWith(task => {
        foreach (IListBlobItem item in task.Result.Results)
        {
            CloudBlockBlob blob = item as CloudBlockBlob;
            Console.WriteLine(blob.Name); // this statement causes the error
        }
    });
    taskList.Remove(completedTask);
}

产生的错误: '对象引用未设置为对象的实例。'

为什么会产生错误,我该如何解决?

ContinueWith方法中指定的任务应该是在前面的任务完成后才执行的,所以我不明白是什么问题。

附带说明,如果删除包含 WriteLine 方法的语句,则代码运行。

【问题讨论】:

  • 很可能,item 不是 CloudBlockBlob 对象。它甚至可以为空。这取决于 BlobResultSegment.Results 的实现。
  • 嗯,你还没有告诉发生这种情况的行。您发布的代码有多个可能发生空引用异常的点。 task.Result.Results 如果 task.Resultnull 这将抛出上述错误。 item as CloudBlockBlob 这将返回 null 如果 item 不是 CloudBlockBlob 类型,如果发生这种情况下一行 blob.Name 将抛出上述错误。
  • 你为什么还要在这里使用ContinueWith?它不应该与错误有关,但你只是让你的逻辑更复杂。您已经等待任务,只需在其下方添加您需要的代码,它也会在任务完成后发生。正如@Rubidium37 所说,您看到的问题可能是由于as 演员。您应该从不使用as,然后“假设”它是那种类型,因为as 可以返回null。如果您确定对象必须是给定类型,或者处理它可能是另一种类型的情况,请使用普通转换。
  • 出于好奇,您的代码暗示您希望同时运行所有任务,并按顺序运行它们的所有延续(一个接一个的延续,没有重叠)。这是故意的吗?

标签: c# async-await task


【解决方案1】:

你不应该使用ContinueWith;这是一种危险的低级方法。

另外,我强烈建议不要使用“WhenAny 然后将其从列表中删除”的方法。这几乎是一种反模式。如果你引入一个辅助异步方法,代码会简单得多。

关于 Azure 特定的问题。首先,ListBlobsSegmentedAsync 的工作方式是您需要一次调用一个,使用每次调用的结果更新令牌,如下所示:

List<IListBlobItem> blobs = new();
BlobContinuationToken token = null;
do
{
  var result = await blobContainer.ListBlobsSegmentedAsync(token);
  blobs.AddRange(result.Results);
  token = result.ContinuationToken;
} while (token != null);

foreach (var item in blobs)
{
  CloudBlockBlob blob = item as CloudBlockBlob;
  Console.WriteLine(blob.Name);
}

至于空引用异常,很可能是因为存在不是CloudBlockBlob 的blob,例如CloudBlobDirectory。我会推荐这样的东西:

foreach (var item in blobs)
{
  if (item is CloudBlob blob)
    Console.WriteLine(blob.Name);
  else if (item is CloudBlobDirectory dir)
    Console.WriteLine("Dir: " + dir.Prefix);
}

【讨论】:

    猜你喜欢
    • 2015-08-04
    • 2015-07-18
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 2011-04-11
    • 1970-01-01
    • 2020-02-28
    • 1970-01-01
    相关资源
    最近更新 更多