我有以下代码,我尝试使用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;
}
}
..偶尔我会在ThreadAbortException
得到readResponseTask.Result
,如此:
System.Threading.ThreadAbortException:线程正在中止。在 System.Threading.Monitor.ObjWait(布尔值exitContext,Int32 millisecondsTimeout,Object obj)at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, 取消语音取消语言) System.Threading.Tasks.Task.SpinThenBlockingWait(的Int32 millisecondsTimeout,CancellationToken cancellationToken)at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, 取消语音取消语言) System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, 取消语音取消语言)
.Result
会在什么情况下抛出这样的例外?我已尝试模拟远程端点上的超时,但我在.Wait()
而不是.Result
处获得了异常。由于异常发生在.Wait()
之后,我假设结果已经从远程站点返回,但在尝试访问结果时出现了某些问题。
任何线索?它可能与线程并发有关吗?
答案 0 :(得分:6)
我会在readResponseTask.Result
中得到ThreadAbortException
不,你没有。调用堆栈清楚地表明它实际上是产生异常的Wait()调用。注意跟踪中经常出现“等待”一词。
很难看出你是如何感到困惑的。请记住,Task.Result属性getter是very small,当您运行程序的Release版本时,它将被内联。所以你永远可以在堆栈跟踪中看到它。
也许只需删除Wait()调用就可以领先。没必要,Result属性getter已经在必要时执行等待。
答案 1 :(得分:3)
在Wait()期间,线程从外部中止。没有人能确定原因。
启用network client trace有助于检测根本原因。
答案 2 :(得分:2)
首先,如果您要立即致电.*Async()
,请不要使用.Wait()
。这是不好的做法,很可能会导致错误的结果。而是使用调用的同步版本client.Get(relativeUri)
和以下内容:
TResult value = response.Content.ReadAs<TResult>();
return value;
如果您打算不利用.NET框架异步编程模型。
但是,如果您希望利用异步I / O功能,则应遵循最佳实践。使用async/await keywords并使您的方法看起来好像是同步的,同时利用.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.ResetAbort来取消中止,所以无法保证线程将永远结束。如果要等到中止的线程结束,可以调用Thread.Join方法。 Join是一个阻塞调用,在线程实际停止执行之前不会返回。
话虽如此,如果某事正在调用.Abort()
并导致此异常。没有什么可以阻止它。无论如何,请尝试遵循最佳实践。
答案 3 :(得分:0)
我在通过Unit-Tests测试方法时遇到了同样的问题,该方法是将数据发送到WebService。 (我有更好的方法可以做到这一点)
问题是Unit-Test调用了我的异步方法,但没有等待unitService WebService调用完成。这中止了WebService调用,我得到了value
。
ThreadAbortException
我需要做的就是通过添加关键字[TestMethod]
public void WebServiceTest01(){
BusinessLogic bs = new BusinessLogic();
bs.CallWebService();
}
使单元测试等到WebService调用完成。
await