Можно ли проверить, запущен ли метод с захваченным SynchronizationContext при запуске Задачей? - PullRequest
0 голосов
/ 27 февраля 2019

Я мог бы где-то пропустить ответ, или это что-то тривиальное, но я не нашел его.

Вот то, что я пытаюсь сделать в коде:

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 ]

0 голосов
/ 28 февраля 2019

Цель состоит в том, чтобы отсеять неправильное использование ConfigureAwait в очень большой базе кода.

Некоторые команды решают использовать инструмент анализа кода для этого.Есть несколько доступных.Самый распространенный подход, который я видел, это требовать a 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 является хорошим решением для модульных тестов, но его было бы неудобно использовать во время выполнения, поскольку у вас естьнекоторые методы, которые нуждаются в контексте.По этой причине большинство групп предпочитают полагаться на проверку кода и / или инструменты анализа кода.

0 голосов
/ 27 февраля 2019

Создайте SynchronizationContext, который выбрасывает исключение при реализации Post и Send.В качестве альтернативы, он может установить логическое значение, указывающее, был ли вызван Send или Post, что позволяет позже проверить это логическое значение (если вы сделаете это, вы, вероятно, захотите запустить предоставленный делегат, в противном случае вы могли бы рискнуть зайти в тупик).

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

...