Можно ли проверить, запущен ли метод с захваченным 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 был вызван, что позволило вам проверить это логическое значение позже (если вы сделаете это, вы, вероятно, захотите запустить предоставленный делегат, иначе вы могли бы рискнуть зайти в тупик).

Установите экземпляр этого пользовательского контекста синхронизации в качестве текущего контекста синхронизации в начале теста, когда тестируете метод, который никогда не должен использовать текущий контекст синхронизации.

Другие вопросы по тегам