【发布时间】:2018-01-28 18:15:22
【问题描述】:
我正在使用 .Net Standard 为第 3 方 API 编写一个类库包装器,稍后我计划在我的其他项目中使用这个包装器。在浏览网络时,我发现人们普遍担心应该使用 HttpClient 类来发出 HTTP 请求。
我知道我可以采取两种方法:
- 一直使用 async/await 到客户端项目
- 使用 Task.Wait() 方法
到目前为止,我将采用第一种方法。但这两种方法似乎都有问题。第一个比同步方法更难重用,同步方法返回它们的类型而不是 Task 对象。我宁愿采用第二种方法,但是容易出现死锁。
我的单次请求代码(HttpClient用无参数构造函数初始化):
protected async Task<JObject> Request(string url)
{
Uri uri = BuildUrl(url);
HttpResponseMessage response = await HttpClient.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return JObject.Parse(result);
}
return new JObject();
}
对于多个请求:
protected async Task<JObject[]> Request(IEnumerable<string> urls)
{
var requests = urls.Select(Request);
return await Task.WhenAll(requests);
}
以及在包装类中的用法:
protected async Task<JObject> RequestGet(string id, bool byUrl)
{
if (IsBulk && !byUrl)
return await Request($"{Url}?id={id}");
if (byUrl)
return await Request(Url + id);
return await Request(Url);
}
如何修改代码(第一个和第二个 sn-p),使其不会在每个调用方方法(第三个 sn-p)中导致任何死锁和异步使用?
【问题讨论】:
-
“第一个方法的可重用性不如同步方法”...我强烈认为这不是真的。在某处你正在做一些 IO 操作,async/await 绝对是在不阻塞的情况下处理这个问题的最简单方法。否则,您将不得不编写“APM 样式”并手动构建您的状态机或阻塞 IO。
-
HttpClient 是一个异步 API,旨在异步使用。长话短说,你不应该一直使用它 async 并避免阻塞同步调用或冒险死锁。
-
@ScottPerham 正如我现在所看到的那样,这种方法将使每个依赖于此类库的系统完全异步或他们将处理与我完全相同的问题我正在努力寻找解决方案。
-
是的,但是他们可以选择使用“一直异步”(如 McGuireV10 所说),将其包装在 Task.Run 中,调用 Wait(),...使用取决于用例。作为库作者,您需要保持一致并使用“已知”的异步概念,例如 Task 是一个很好的方法(当然在我看来!)
-
@gosferano 这么说吧:如果你只使用异步并且你的库的用户使用 Wait() 并且他们遇到了死锁,那是他们的错。但是,如果您在库中使用 Wait() 并且出现死锁,那是您的错。 :)
标签: c# .net asynchronous dotnet-httpclient .net-standard