调用Task.Result时出现ThreadAbortException
本文关键字:ThreadAbortException Task Result 调用 | 更新日期: 2023-09-27 18:21:42
我有以下代码,我试图使用HttpClient
:向远程端点发出请求
using (var client = new HttpClient())
{
client.BaseAddress = _serviceBaseAddress;
Task<HttpResponseMessage> readResponseTask = client.GetAsync(relativeUri);
readResponseTask.Wait();
using (var response = readResponseTask.Result)
{
if (response.StatusCode == HttpStatusCode.NotFound || !response.IsSuccessStatusCode)
{
return default(TResult);
}
Task<TResult> readContentTask = response.Content.ReadAsAsync<TResult>();
readContentTask.Wait();
TResult value = readContentTask.Result;
return value;
}
}
有时我会在readResponseTask.Result
上得到ThreadAbortException
,就像这样:
System.Threading.ThreadAbortException:线程被中止。在System.Threading.Monitor.OjWait(布尔exitContext,Int32毫秒超时,对象obj)System.Threading.ManualResetEventSlim.Wait(Int32毫秒超时,CancellationToken取消令牌)System.Threading.Tasks.Task.SpinThenBlockingWait(Int32毫秒超时,CancellationToken取消令牌)System.Threading.Tasks.Task.InteralWait(Int32毫秒超时,CancellationToken取消令牌)System.Threading.Tasks.Task.Wait(Int32毫秒超时,CancellationToken CancellationToken)
在什么情况下.Result
会抛出这样的异常?我曾尝试在远程端点上模拟超时,但在.Wait()
而不是.Result
处出现异常。由于异常发生在.Wait()
之后,我假设已经从远程站点返回了结果,但在尝试访问结果时不知怎么出了问题。
有线索吗?这可能与线程并发有关吗?
我会在readResponseTask中得到ThreadAbortException。Result
不,你没有。调用堆栈清楚地表明,实际上是Wait()调用产生了异常。请注意"等待"一词在跟踪中的频繁出现。
很难看出你是怎么弄糊涂的。请记住,Task.Result属性getter非常小,并且将在运行程序的Release构建时内联。因此,您永远无法在堆栈跟踪中看到它。
也许您只需删除Wait()调用就可以了。没有必要,Result属性getter已经在必要时执行了等待。
Wait()期间,线程从外部中止。没有人能确定为什么。
启用网络客户端跟踪可以帮助检测根本原因。
首先,如果要立即调用.Wait()
,请不要使用.*Async()
。这是一种糟糕的做法,很可能会导致错误的结果。相反,使用调用的同步版本client.Get(relativeUri)
和以下内容:
TResult value = response.Content.ReadAs<TResult>();
return value;
如果您不打算利用.NET框架异步编程模型。
但是,如果您更愿意利用异步I/O功能,则应该遵循最佳实践。使用async/await关键字,使方法看起来像是同步的,同时利用.NET框架的异步关键字的功能。
我只能想象你的方法的入口点看起来是这样的:
public TResult InvokeClientGet<TResult>(string relativeUri)
{
// ... left out for brevity
}
这实际上阻止了您使用async/await关键字。相反,请尝试以下操作:
public async Task<TResult> InvokeClientGet<TResult>(string relativeUri)
{
try
{
using (var client = new HttpClient { BaseAddress = _serviceBaseAddress })
{
using (var response = await client.GetAsync(relativeUri))
{
if (response.StatusCode == HttpStatusCode.NotFound ||
!response.IsSuccessStatusCode)
{
return default(TResult);
}
return await response.Content.ReadAsAsync<TResult>();
}
}
catch (Exception ex)
{
// Handle exceptional conditions
}
}
关于System.Threading.ThreadAbortException.的几句话
当调用Abort方法来销毁线程时,公共语言运行库会抛出ThreadAbortException。ThreadAbortException是一个可以捕获的特殊异常,但它将在catch块结束时自动再次引发。当引发此异常时,运行时会在结束线程之前执行所有finally块。因为线程可以在finally块中进行无边界计算,或者调用thread.ResetArtrt来取消中止,所以无法保证线程会结束。如果要等待中止的线程结束,可以调用thread.Join方法。Join是一个阻塞调用,在线程实际停止执行之前不会返回。
话虽如此,如果有什么东西正在调用.Abort()
并导致此异常。你没什么办法可以阻止它。无论如何,试着遵循最佳实践。
我在通过单元测试向WebService发送数据的方法时遇到了同样的问题。(ik有更好的方法可以做到这一点)
问题是单元测试调用了我的异步方法,但没有等到WebService调用完成。这中止了WebService调用,我得到了一个ThreadAbortException
。
[TestMethod]
public void WebServiceTest01(){
BusinessLogic bs = new BusinessLogic();
bs.CallWebService();
}
我所需要做的就是添加关键字await
,让单元测试等待WebService调用完成。
[TestMethod]
public void WebServiceTest01(){
BusinessLogic bs = new BusinessLogic();
var result = await bs.CallWebService();
Assert.NotNull(result);
}