На SO есть ряд вопросов о том, как избежать взаимоблокировок в асинхронном коде (например, методы HttpClient
), вызываемые из кода синхронизации, например this, Я знаю о различных способах избежать этих взаимоблокировок.
В отличие от этого, я хотел бы узнать о стратегиях, чтобы усугубить или вызвать эти взаимоблокировки в неисправном коде во время тестирования.
Вот примерный бит плохого кода, который недавно вызвал проблемы для нас:
public static string DeadlockingGet(Uri uri)
{
using (var http = new HttpClient())
{
var response = http.GetAsync(uri).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
Он вызывался из приложения ASP.NET и, таким образом, имел значение null
SynchronizationContext.Current
, которое обеспечивало топливо для потенциального тупика.
Помимо вопиющее злоупотребление HttpClient, этот код зашел в тупик на одном из наших серверов компании... но только спорадически.
Моя попытка воспроизвести тупик
Я работаю в QA, поэтому я попытался воспроизвести тупик через unit test, который попадает в локальный экземпляр порта прослушивателя Fiddler:
public class DeadlockTest
{
[Test]
[TestCase("http://localhost:8888")]
public void GetTests(string uri)
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var context = SynchronizationContext.Current;
var thread = Thread.CurrentThread.ManagedThreadId;
var result = DeadlockingGet(new Uri(uri));
var thread2 = Thread.CurrentThread.ManagedThreadId;
}
}
Несколько замечаний:
-
По умолчанию unit test имеет значение null
SynchronizationContext.Current
, и поэтому.Result
фиксирует контекстTaskScheduler
, который это контекст пула потоков. Поэтому я используюSetSynchronizationContext
, чтобы установить его в конкретный контекст, чтобы более точно подражать тому, что происходит в контексте ASP.NET или UI. -
Я настроил Fiddler на некоторое время (~ 1 минута), прежде чем ответить. Я слышал от коллег, что это может помочь воспроизвести тупик (но у меня нет веских доказательств, что это так).
-
Я запускал его с помощью отладчика, чтобы убедиться, что
context
- неnull
иthread == thread2
.
К сожалению, мне не повезло, вызвав тупики с помощью этого unit test. Он всегда заканчивается, независимо от того, как долго задерживается Fiddler, если задержка превышает 100-секундный по умолчанию Timeout
HttpClient
(в этом случае он просто взрывается с исключением).
Мне не хватает ингредиента для зажигания тупика? Я хотел бы воспроизвести тупики, чтобы быть уверенным, что наше возможное исправление действительно работает.