1

我正在尝试实现这个简单的任务,即在 C#4 中异步列出 AmazonS3 存储桶中的所有对象以及分页请求。我使用以下代码段在 C#5 中工作:

var listRequest = new ListObjectsRequest().WithBucketName(bucketName);
ListObjectsResponse listResponse = null;
var list = new List<List<S3Object>>();

while (listResponse == null || listResponse.IsTruncated)
{
    listResponse = await Task<ListObjectsResponse>.Factory.FromAsync(
        client.BeginListObjects, client.EndListObjects, listRequest, null);

    list.Add(listResponse.S3Objects);

    if (listResponse.IsTruncated)
    {
        listRequest.Marker = listResponse.NextMarker;
    }
}

return list.SelectMany(l => l);

我正在异步调用 BeginListObjects/EndListObjects 对,但是每次响应说它被截断时,我都必须重复该调用。这段代码对我有用。

但是,我现在想在 C#4 的 TPL 中执行此操作,我没有使用 async/await 的奢侈,并且想了解这是否可以使用延续来完成。

我如何在 C#4 中做同样的事情?

4

1 回答 1

4

好的,因此,与其将项目与每个任务/延续一起放入列表中,不如在非等待模型中让每个任务/延续返回整个序列更容易。鉴于此,我使用以下辅助方法将每个人的迭代结果添加到汇总总数中。

public static Task<IEnumerable<T>> Concat<T>(Task<IEnumerable<T>> first
        , Task<IEnumerable<T>> second)
{
    return Task.Factory.ContinueWhenAll(new[] { first, second }, _ =>
    {
        return first.Result.Concat(second.Result);
    });
}

接下来,我使用 follow 方法将单个结果的任务转换为序列的任务(仅包含一项)。

public static Task<IEnumerable<T>> ToSequence<T>(this Task<T> task)
{
    var tcs = new TaskCompletionSource<IEnumerable<T>>();
    task.ContinueWith(_ =>
    {
        if (task.IsCanceled)
            tcs.SetCanceled();
        else if (task.IsFaulted)
            tcs.SetException(task.Exception);
        else
            tcs.SetResult(Enumerable.Repeat(task.Result, 1));
    });

    return tcs.Task;
}

请注意,您有一些未定义的字段/本地;我假设您可以毫无困难地将它们添加到适当的方法中。

private Task<IEnumerable<S3Object>> method(object sender, EventArgs e)
{

    ListObjectsResponse listResponse = null;
    return Task<ListObjectsResponse>.Factory.FromAsync(
        client.BeginListObjects, client.EndListObjects, listRequest, null)
        .ToSequence()
        .ContinueWith(continuation);
}

这是真正的魔法发生的地方。基本上,

public Task<IEnumerable<S3Object>> continuation(Task<IEnumerable<S3Object>> task)
{
    if (task.Result == null)  //not quite sure what null means here//may need to edit this recursive case
    {
        return Task<ListObjectsResponse>.Factory.FromAsync(
                client.BeginListObjects, client.EndListObjects, listRequest, null)
                .ToSequence()
                .ContinueWith(continuation);
    }
    else if (task.Result.First().IsTruncated)
    {
        //if the results were trunctated then concat those results with 
        //TODO modify the request marker here; either create a new one or store the request as a field and mutate.
        Task<IEnumerable<S3Object>> nextBatch = Task<ListObjectsResponse>.Factory.FromAsync(
                client.BeginListObjects, client.EndListObjects, listRequest, null)
                .ToSequence()
                .ContinueWith(continuation);
        return Concat(nextBatch, task);//recursive continuation call
    }
    else //if we're done it means the existing results are sufficient
    {
        return task;
    }
}
于 2012-11-19T21:05:02.453 回答