Можно ли проверить, запущен ли метод с захваченным SynchronizationContext при запуске Задачей?
Я мог бы где-то пропустить ответ, или это что-то тривиальное, но я не нашел его.
Вот что я пытаюсь сделать в коде:
public static async Task CapturesContext()
{
await Task.Run(() => DoWhatever());
}
public static async Task DoesNotCaptureContext()
{
await Task.Run(() => DoWhatever()).ConfigureAwait(false);
}
public static void DoWhatever()
{
//Any way to test here that it was run with/without a captured SynchronizationContext?
}
Выше приведен очень упрощенный пример, но он выражает то, чего я хочу достичь.
Цель состоит в том, чтобы отсеять неправильное использование ConfigureAwait
в очень большой кодовой базе.
Учитывая любой метод (ы), которые запускаются с использованием Task
Можно ли проверить, с помощью кода, как Assert
или модульный тест, выполняется ли данный метод с использованием захваченного SynchronizationContext
?
Если нет, есть ли какой-нибудь альтернативный способ достижения моей цели?
2 ответа
Цель состоит в том, чтобы отсеять неправильное использование ConfigureAwait в очень большой кодовой базе.
Некоторые команды выбирают для этого инструмент анализа кода. Есть несколько доступных. Наиболее распространенный подход, который я видел, это требовать ConfigureAwait
для каждого await
и явно указать либо true
или же false
, Это гарантирует, что каждый await
был рассмотрен, и поток контекста явно. Другие команды применяют специфичные для проекта правила "всегда использовать" ConfigureAwait(false)
"и просто зависит от проверки кода для проектов, которые не могут следовать этому правилу.
Проблема с вашим примером кода заключается в том, что это невозможно для DoWhatever
знать, был ли он вызван косвенно, из-за Task.Run
, Если вы переписываете эти методы, становится ясно:
public static async Task CapturesContext()
{
var task = Task.Run(() => DoWhatever());
await task;
}
public static async Task DoesNotCaptureContext()
{
var task = Task.Run(() => DoWhatever());
var configuredTask = task.ConfigureAwait(false);
await configuredTask;
}
Первые строки переписанных методов должны прояснить, что DoWhatever
понятия не имеет, CapturesContext
или же DoesNotCaptureContext
будет захватывать контекст или нет. Обратите внимание на "волю" (будущее время) - вполне возможно, что DoWhatever
работает и заканчивает выполнение раньше ConfigureAwait(false)
даже называется.
Теперь вы можете проверить изнутри задачи, выполняется ли она сейчас в контексте. Но в этом случае для обоих примеров методов, DoWhatever
не увидит контекст из-за Task.Run
, Так что это не поможет вам обнаружить тот факт, что CapturesContext
действительно захватывает контекст; DoWhatever
не видит контекст, поэтому не может его обнаружить.
Обычай SynchronizationContext
является хорошим решением для модульных тестов, но его было бы неудобно использовать во время выполнения, поскольку у вас есть некоторые методы, которым нужен контекст. По этой причине большинство команд предпочитают полагаться на проверку кода и / или инструменты анализа кода.
Создать SynchronizationContext
что создает исключение в реализации Post
и отправить. В качестве альтернативы, он должен установить логическое значение, указывающее, Send
или же Post
был вызван, что позволило вам проверить это логическое значение позже (если вы сделаете это, вы, вероятно, захотите запустить предоставленный делегат, иначе вы могли бы рискнуть зайти в тупик).
Установите экземпляр этого пользовательского контекста синхронизации в качестве текущего контекста синхронизации в начале теста, когда тестируете метод, который никогда не должен использовать текущий контекст синхронизации.