2

MonoTouch アプリで DropBox API に非同期でアクセスしたいと考えています。それ自体がRestSharpに依存しているDropNet
を使用すると便利だと思いました。

どちらのライブラリもうまく機能しますが、Tasks を返す DropNet オーバーロードでは、リクエストをキャンセル トークンに関連付ける方法がありません。

これは、それらの実装がどのように見えるかです:

public Task<IRestResponse> GetThumbnailTask(string path, ThumbnailSize size)
{
    if (!path.StartsWith("/")) path = "/" + path;
    var request = _requestHelper.CreateThumbnailRequest(path, size, Root);
    return ExecuteTask(ApiType.Content, request, cancel);
}

ExecuteTask実装は Laurent Kempé に基づいてTaskCompletionSourceおり、もともとLaurent Kempé によって書かれました:

public static class RestClientExtensions
{
    public static Task<TResult> ExecuteTask<TResult>(
        this IRestClient client, IRestRequest request
        ) where TResult : new()
    {
        var tcs = new TaskCompletionSource<TResult>();

        WaitCallback asyncWork = _ => {
            try {
                client.ExecuteAsync<TResult>(request,
                    (response, asynchandle) => {
                        if (response.StatusCode != HttpStatusCode.OK) {
                            tcs.SetException(new DropboxException(response));
                        } else {
                            tcs.SetResult(response.Data);
                        }
                    }
                );
            } catch (Exception exc) {
                    tcs.SetException(exc);
            }
        };

        return ExecuteTask(asyncWork, tcs);
    }


    public static Task<IRestResponse> ExecuteTask(
        this IRestClient client, IRestRequest request
        )
    {
        var tcs = new TaskCompletionSource<IRestResponse>();

        WaitCallback asyncWork = _ => {
            try {
                client.ExecuteAsync(request,
                    (response, asynchandle) => {
                        if (response.StatusCode != HttpStatusCode.OK) {
                            tcs.SetException(new DropboxException(response));
                        } else {
                            tcs.SetResult(response);
                        }
                    }
                );
            } catch (Exception exc) {
                    tcs.SetException(exc);
            }
        };

        return ExecuteTask(asyncWork, tcs);
   }

    private static Task<TResult> ExecuteTask<TResult>(
        WaitCallback asyncWork, TaskCompletionSource<TResult> tcs
        )
    {
        ThreadPool.QueueUserWorkItem(asyncWork);
        return tcs.Task;
    }
}

でのキャンセルをサポートするためにこのコードを変更または拡張するにはどうすればよいCancellationTokenですか?
私はそれを次のように呼びたいです:

var task = dropbox.GetThumbnailTask(
    "/test.jpg", ThumbnailSize.ExtraLarge2, _token
);
4

1 に答える 1

1

は値型であるためCancellationToken、デフォルト値を使用して API にオプションのパラメーターとして追加し、null チェックを回避できます。これは便利です。

public Task<IRestResponse> GetThumbnailTask(
    string path, ThumbnailSize size, CancellationToken cancel = default(CancellationToken)
) {
    if (!path.StartsWith("/")) path = "/" + path;
    var request = _requestHelper.CreateThumbnailRequest(path, size, Root);
    return ExecuteTask(ApiType.Content, request, cancel);
}

現在、RestSharpメソッドは、基になるをメソッドとともにカプセル化してExecuteAsync返します。これが物事をキャンセルする方法です。RestRequestAsyncHandleHttpWebRequestAbort

public static Task<TResult> ExecuteTask<TResult>(
    this IRestClient client, IRestRequest request, CancellationToken cancel = default(CancellationToken)
    ) where TResult : new()
{
    var tcs = new TaskCompletionSource<TResult>();
    try {
        var async = client.ExecuteAsync<TResult>(request, (response, _) => {
            if (cancel.IsCancellationRequested || response == null)
                return;

            if (response.StatusCode != HttpStatusCode.OK) {
                tcs.TrySetException(new DropboxException(response));
            } else {
                tcs.TrySetResult(response.Data);
            }
        });

        cancel.Register(() => {
            async.Abort();
            tcs.TrySetCanceled();
        });
    } catch (Exception ex) {
        tcs.TrySetException(ex);
    }

    return tcs.Task;
}

public static Task<IRestResponse> ExecuteTask(this IRestClient client, IRestRequest request, CancellationToken cancel = default(CancellationToken))
{
    var tcs = new TaskCompletionSource<IRestResponse>();
    try {
        var async = client.ExecuteAsync<IRestResponse>(request, (response, _) => {
            if (cancel.IsCancellationRequested || response == null)
                return;

            if (response.StatusCode != HttpStatusCode.OK) {
                tcs.TrySetException(new DropboxException(response));
            } else {
                tcs.TrySetResult(response);
            }
        });

        cancel.Register(() => {
            async.Abort();
            tcs.TrySetCanceled();
        });
    } catch (Exception ex) {
        tcs.TrySetException(ex);
    }

    return tcs.Task;
}

最後に、Lauren の実装は要求をスレッド プールに入れますが、そうする理由がわかりません。<code>ExecuteAsync 自体が非同期です。だから私はそれをしません。

以上で、DropNet 操作をキャンセルできます。

私はまた、あなたに役立つかもしれないいくつかの微調整を行いました.

呼び出しを別の sTaskにラップする以外にDropBox s をスケジュールする方法がなかったため、リクエストの最適な同時実行レベルを見つけることにしました。ExecuteTaskTask4

static readonly Uri DropboxContentHost = new Uri("https://api-content.dropbox.com");

static DropboxService()
{
    var point = ServicePointManager.FindServicePoint(DropboxContentHost);
    point.ConnectionLimit = 4;
}

また、ハンドルされていないタスクの例外を地獄で腐らせることに満足していたので、それが私がしたことです:

TaskScheduler.UnobservedTaskException += (sender, e) => {
    e.SetObserved();
};

最後の観察事項として、タスクはすぐに開始されるため、によって返されたタスクを呼び出すべきではありません。Start()DropNetそれが気に入らない場合は、ExecuteTaskによってサポートされていないさらに別の「実際の」タスクをラップする必要がありますTaskCompletionSource

于 2012-11-12T15:52:21.297 に答える