gpt4 book ai didi

c# - 如何在 DropNet 中实现 CancellationToken 支持?

转载 作者:太空宇宙 更新时间:2023-11-03 13:52:22 25 4
gpt4 key购买 nike

我想在 MonoTouch 应用程序中异步访问 DropBox API。
我认为使用 DropNet 会很方便它本身依赖于 RestSharp .

这两个库都运行良好,但返回 Task 的 DropNet 重载不会为您提供将请求与取消 token 相关联的方法。

这是他们的实现方式:

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 实现基于 TaskCompletionSourcewas原本 written by 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
);

最佳答案

因为 CancellationToken 是一个值类型,我们可以将它作为可选参数添加到具有默认值的 API 中,避免空检查,这很不错。

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 方法返回封装底层 HttpWebRequestRestRequestAsyncHandle,以及 Abort 方法。这就是我们取消事情的方式。

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's implementation将请求放入线程池,但我看不出这样做的理由——ExecuteAsync 本身是异步的。所以我不会那样做。

这就是取消 DropNet 操作。

我还做了一些可能对您有用的调整。

因为我无法在不求助于将 ExecuteTask 调用包装到另一个 Task 的情况下安排 DropBox Task,所以我决定找一个请求的最佳并发级别,对我来说是 4,并明确设置它:

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

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

我也满足于让未处理的任务异常在 hell 中腐烂,所以这就是我所做的:

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

最后一个观察是,您不应该对 DropNet 返回的任务调用 Start(),因为该任务会立即开始。如果您不喜欢那样,则需要将 ExecuteTask 包装在另一个不受 TaskCompletionSource 支持的“真实”任务中。

关于c# - 如何在 DropNet 中实现 CancellationToken 支持?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13346912/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com