NullReferenceException в System.Threading.Tasks, вызывающий HttpClient.GetAsync(url)
У меня странная проблема в моем приложении MVC 4.0. Я использую веб-службы REST (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-адресе. Сделано немного Unit Test, отлично работает с тем же конкретным URL-адресом...
Как он бомбит в отладчике, трудно понять, что случилось. (Исключений нет!). Наконец, я видел с IntelliTrace, что есть исключения, кажущиеся внутри System.Threading.Tasks
. Трудно указать, как вызов Stack немного запутан для моих глазных экспертов...
Вот стек вызовов, который я получаю от предыдущего прохода в коде:
> 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#
В любом случае это выглядит определенно связанным с задачами, Async, фоновыми работниками и т.д. Есть ли хороший способ "очистить" все остальные запущенные задачи, чтобы избежать этой проблемы?
Спасибо за вашу помощь,
Бернард.
Ответы
Ответ 1
Добавляя к @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 пыталась воссоединиться с контекстом, который был заглушен, поэтому он выбрасывал исключение NullReferenceException вне try/catch.
Это очень трудно диагностировать. В Production вы ничего не увидите в try/catch, а в Visual Studio, который ожидает, что запланировано вернуться к исходному контексту, будет несколько случайным - это зависит от того, что решает TaskScheduler.
Если вы не хотите запускать и забывать очевидный ответ - ждать метода async - у вас будет предупреждение о компиляторе, напоминающее вам об этом.
Если вы хотите запустить и забыть решение, нужно явно запустить новую задачу. Лучший способ сделать это в этом ответе о пожаре и забыть Задачи.
Ответ 2
По внешнему виду ваш код может завершиться до того, как один из потоков, посланных спать в событии исключения из первого блока try, будет разрешен, поэтому через 5 минут, когда они просыпаются, нет исходного потока для rejoin, в результате чего NullReferenceException из AssociateWithCurrentThread
Ответ 3
Когда я получал стек исключений и вызовов, показанный выше, это было потому, что я пытался выполнить "огонь и забыть" выполнение с помощью async, что было очень плохой идеей. Я переключился на прядение новой нити за то, что я хотел, и аварии исчезли.