1

私は、AmazonS3 バケット内のすべてのオブジェクトをページングされたリクエストで非同期的にリストするという簡単なタスクを C#4 で実装しようとしています。次のスニペットを使用して、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 メソッドを使用して、単一の結果のタスクを取得し、それをシーケンスのタスク (その 1 つの項目のみを含む) に変換しました。

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 に答える