我的MVC 4.0应用程序中有一个奇怪的问题。我使用REST Web服务(Amazon Associate)。我创建了一个方法,我从各处使用它。缩短版本是这样的:
private async Task<XElement> GetRequest(string url)
{
string myresponse;
HttpResponseMessage response = null;
HttpClient client = new HttpClient();
try
{
response = await client.GetAsync(url);
myresponse = response.Content.ToString();
if (myresponse.Contains("503"))
{
myTrace.WriteLine("503 Sleep.....");
Thread.Sleep(3000); // looks like amazon us does not like fast requests....
return await GetRequest(url); //restart after pausing....
}
}
catch (TaskCanceledException ex)
{
myTrace.WriteLine("TaskCancelled From GetRequest: " + ex);
return null;
}
catch (HttpRequestException ex)
{
myTrace.WriteLine("RequestException Sleep.....");
Thread.Sleep(300000); // 5 minutes de pause
}
catch (Exception ex)
{
myTrace.WriteLine("From GetRequest: " + ex);
return null;
}
try
{
XElement content = await response.Content.ReadAsAsync<XElement>();
response.Dispose();
client.Dispose();
return content;
}
catch (Exception)
{
return null;
}
}
没什么特别的,它确实运作得很好......但是,现在,在一个特定的电话中,它会在client.GetAsync(url)
上轰炸。起初我怀疑网址中的内容是错误的,所以我从调试器会话中抓取它并将其直接粘贴到我的浏览器中,得到了预期的答案......
所以,URL没有错。做了一个小单元测试,使用相同的特定URL工作得很好......
因为它在调试器中炸弹,很难看出什么是错的。 (没有抛出异常!)。最后,我在IntelliTrace中看到有异常,似乎在System.Threading.Tasks
内。很难确定点,因为调用Stack对我的NON专家眼睛来说有点混乱......
这是我从代码中的上一个传递获得的调用堆栈:
> System.Web.dll!System.Web.ThreadContext.AssociateWithCurrentThread(bool setImpersonationContext = {unknown}) C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnterPrivate(bool setImpersonationContext = {unknown}) C#
System.Web.dll!System.Web.HttpApplication.OnThreadEnter() C#
System.Web.dll!System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter() C#
System.Web.dll!System.Web.Util.SynchronizationHelper.SafeWrapCallback(System.Action action = {unknown}) C#
System.Web.dll!<>c__DisplayClass9.AnonymousMethod(System.Threading.Tasks.Task _ = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.ContinuationTaskFromTask.InnerInvoke() C#
mscorlib.dll!System.Threading.Tasks.Task.Execute() C#
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj = {unknown}) C#
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C#
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution = {unknown}) C#
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() C#
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() C#
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() C#
无论如何,这看起来肯定与任务,异步,后台工作者等有关......是否有一种很好的方法来“清除”所有其他正在运行的任务,以避免这个问题?
感谢您的帮助, 伯纳德。
答案 0 :(得分:20)
再加上@ kcar的答案,我有一个非常类似的问题,在代码路径上有多个等待,有一个没有等待的方法,如:
public async Task JsonResult BookThing(InputModel model)
{
// Do some stuff
thisIsAnAsyncMethod(Model model); // Fire and forget
return Json(null);
}
protected async Task thisIsAnAsyncMethod(Model model)
{
await oneThing();
await anotherThing();
await somethingElse();
}
这导致等待随机失败而不让我捕获异常 - 因为TPL试图重新加入已被淘汰的Context,所以它在try / catch之外抛出NullReferenceException。
这很难诊断。在生产中你不会在try / catch中看到任何东西,并且在等待被调度重新加入原始上下文的Visual Studio中有点随机 - 它取决于TaskScheduler碰巧决定做什么。
如果您不想触发并忘记显而易见的答案是等待异步方法 - 您将有一个编译器警告提醒您这样做。
如果您确实想解雇并忘记解决方案是明确启动新任务。最好的方法是covered in this answer about fire and forget Tasks。
答案 1 :(得分:5)
从它的外观来看,你的代码可以在其中一个线程在第一个try块的异常事件中进入睡眠状态之前完成,所以在它们唤醒5分钟之后,就没有原始线程了重新加入导致来自AssociateWithCurrentThread
的NullReferenceException答案 2 :(得分:0)
当我得到上面显示的异常和调用堆栈时,那是因为我试图做一个&#34; fire并忘记&#34;使用异步执行,这是一个非常糟糕的主意。为了我想要的东西,我切换到新线程的旋转,崩溃消失了。