【发布时间】:2019-02-16 03:41:31
【问题描述】:
我正在使用客户端库来访问第 3 方 API。该库由 NSwagStudio 根据 Swagger 文档生成。
我正在开发的应用在所有调用中都是完全同步的,并且将其更新为异步超出了我正在开发的范围。
当我从单元测试中测试客户端库时,它工作正常。当我尝试从 ASP.Net 应用程序中调用它时,我收到以下错误:
CancellationTokenSource 已被释放。
我已将客户端库提炼为演示问题的要点,我选择了一个选项来提供同步方法和异步:
public class ClientApi
{
private readonly HttpClient _httpClient;
public ClientApi(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string BaseUrl { get; set; }
public object Get()
{
return Task.Run(async () => await GetAsync(CancellationToken.None)).GetAwaiter().GetResult();
}
/// <returns>OK</returns>
/// <param name="cancellationToken">
/// A cancellation token that can be used by other objects or threads to receive notice of
/// cancellation.
/// </param>
public async Task<string> GetAsync(CancellationToken cancellationToken)
{
var client_ = _httpClient;
try
{
using (var request_ = new HttpRequestMessage())
{
request_.Method = new HttpMethod("GET");
request_.RequestUri = new System.Uri(BaseUrl, System.UriKind.RelativeOrAbsolute);
var response_ = await client_.SendAsync(
request_,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken
).ConfigureAwait(false);
try
{
// Exception occurs on following line
var responseData_ = response_.Content == null
? null
: await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
return responseData_;
}
finally
{
response_?.Dispose();
}
}
}
finally { }
}
}
这是调用它的代码:
protected void OnClick(object sender, EventArgs e)
{
var httpClient = new HttpClient();
var client = new ClientApi(httpClient)
{
BaseUrl = "https://www.google.com"
};
var html = client.Get();
}
调用这个的代码只是一个带有按钮的asp.net页面,按钮事件运行的代码与通过的单元测试相同。
当我在调试器中比较运行时:从单元测试中,response_.Content 对象没有取消标记,但是当从 asp.net 运行时它有。事实上,它们几乎看起来是不同的对象,尽管 GetType() 将它们都报告为 System.Net.Http.StreamContent。从反编译类来看,它没有 _cancellationtoken 属性,那么调试器从哪里得到它呢?
我猜测对我的 asp.net Web 应用程序的 http 请求有它自己的令牌和源,它以某种方式被 HttpClient 使用。但是,客户端正在等待所有异步调用以同步获取结果,所以我不明白如何处理底层 CTS,因为我们还没有从客户端库的调用中返回。
任何人都可以理解发生了什么并且有解决方案吗?
【问题讨论】:
-
您可能不想同时使用 await + GetResult/.Result。这种组合很容易死锁
-
这不是我的代码,因为它是由 NSwag Studio 生成的。同步调用异步方法以避免潜在死锁@steve 的推荐方法是什么?
-
你没有。一路同步或一路异步。混合它们很容易导致死锁
-
将事件处理程序更改为异步应该很容易。这是一个事件,框架事件可以是 async void 是可以的(实际上这是少数可以的情况之一)。
标签: c# asp.net httpclient cancellation-token sendasync