C#异步方法-需要帮助
本文关键字:帮助 异步方法 | 更新日期: 2023-09-27 18:22:04
我有一个异步方法,它使用Web API操作。它似乎陷入了一个循环。我的理由是,如果我把一个破发点放在接球块的第1线上,然后踩进去,它就永远不会真正击中第二条线。
最初,我返回了一个包含100000行以上(约30mb)行的数据集,并认为由于请求的大小,这可能会很慢,但在更改Web API操作仅返回1行后,问题仍然存在。数据肯定会被返回,因为当我浏览到Web API解决方案的URI时,我会在浏览器中返回JSON。
public async Task<IEnumerable<Document>> GetAll()
{
try
{
var response = await _client.GetAsync(
string.Format("{0}/api/document", _baseURI));
// never hits line below
return await response.Content.ReadAsAsync<Document>()
as IEnumerable<Document>;
}
catch (Exception ex)
{
// handle exception
}
}
我不确定我是不是错过了什么?如有帮助,不胜感激。
编辑1
为了回答一些问题,我有一个Web API项目,它被一个MVC项目引用。我不得不对JSON反序列化的原始问题进行一些更改。
存储库:
public async Task<IEnumerable<Document>> GetAll()
{
try
{
string json;
var response = await _client.GetAsync(string.Format(
"{0}/api/document", _baseURI)).ConfigureAwait(false);
var resource = await response.Content.ReadAsAsync<Document>();
using(var reader = new StreamReader(resource.ToString()))
{
json = reader.ReadToEndAsync().ToString();
}
return JsonConvert.DeserializeObjectAsync<Document>(json)
as IEnumerable<Document>;
}
catch (Exception)
{
throw;
}
}
控制器:
public async Task<ActionResult> GetAll()
{
return PartialView("_GetAllDocumentsPartial", await _docRepo.GetAll());
}
根据以下答案中描述的更改,在进行上述调试时仍然会出现相同的问题。然而,我在存储库中的方法中的catch上得到了"一个任务被取消了。"异常。
堆栈跟踪。
是否从ASP.NET应用程序调用GetAll().Result
?既然你已经在这个问题上标记了MVC,我想是的。如果你这样做了,那么你就是在死锁。
假设你这样调用web API。
public ActionResult Index()
{
var result = GetAll().Result;
return View();
}
这里的问题是,当异步调用完成时,它需要在跳出来的原始线程上继续,但您通过调用Result()
阻塞了该线程。对Result的调用阻塞并等待异步调用返回,异步继续等待ASP.NET线程可以继续。那是一把死锁。
像这样更改GetAll()。
public async Task<IEnumerable<Document>> GetAll()
{
try
{
var response = await _client.GetAsync(
string.Format("{0}/api/document", _baseURI))
.ConfigureAwait(false);
// never hits line below
return await response.Content.ReadAsAsync<Document>()
as IEnumerable<Document>;
}
catch (Exception ex)
{
// handle exception
}
}
这表明当异步调用完成时,上下文不需要保留,因此延续发生在线程池线程(而不是ASP.NET)中
最好将操作方法更改为public async Task<ActionResult> Index()
并等待GetAll()
。
如果你觉得这个答案有帮助,你只需要感谢斯蒂芬·克利里。
您是否在外部库中工作?如果是,请尝试添加ConfigureAwait(false);
var response = await _client.GetAsync(string.Format("{0}/api/document", _baseURI))
.ConfigureAwait(false);
它与告诉等待有关,而不是捕捉当前的上下文。调用ConfigureAwait将确保使用ThreadPool执行方法的其余部分。关于这个主题的一些好的读物可以在斯蒂芬·克利里的博客上找到。
如果它可以与web浏览器一起使用,但不使用代码,那么您可能需要发送一个accept标头。添加以下行
_client.DefaultRequestHeaders.Accept = new MediaTypeWithQualityHeaderValue("application/json");