使用 Task.WhenAny 异步处理任务列表

Process a list of tasks asynchronously using Task.WhenAny

我创建了一个任务列表。一旦列表中的某个任务完成,我想处理它的结果(在本例中,打印 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);
}

产生的错误: 'Object reference not set to an instance of an object.'

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

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

附带说明一下,如果包含 WriteLine 方法的语句被删除,代码就会运行。

你不应该使用 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);
}