由Task启动时,是否可以测试方法是否正在使用捕获的SynchronizationContext运行?

时间:2019-02-27 16:09:05

标签: c# task synchronizationcontext .net-4.7.2

我可能在某处缺少答案,或者这很琐碎,但我没有找到它。

实际上是我要在代码中完成的工作:

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或单元测试之类的代码检查给定的方法是否正在使用捕获的{{ 1}}?

如果没有,我还有其他方法可以实现自己的目标吗?

2 个答案:

答案 0 :(得分:1)

创建一个SynchronizationContext并在实现Post和Send时引发异常。或者,将其设置为一个布尔值,指示是否调用了SendPost,让您以后可以检查该布尔值(如果这样做,则可能要运行提供的委托,否则可能会冒险死锁)。

每当测试不应该使用当前同步上下文的方法时,就在测试开始时将该自定义同步上下文的实例设置为当前同步上下文。

答案 1 :(得分:1)

  

目标是在非常大的代码库中清除对ConfigureAwait的不当使用。

一些团队选择为此使用代码分析工具。有几种可用。我见过的最常见的方法是每个ConfigureAwait 要求一个await,并明确指定truefalse。这样可以确保每个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)运行并完成执行

现在,您可以从任务内检查它是否正在上下文上运行。但是在这种情况下,对于两个示例方法,由于DoWhateverTask.Run将看不到上下文。因此,这无助于您发现CapturesContext确实捕获了上下文的事实; DoWhatever没有看到上下文,因此无法检测到上下文。

自定义SynchronizationContext是单元测试的一个很好的解决方案,但是在运行时使用它会很尴尬,因为您确实有一些需要上下文的方法。因此,大多数团队选择依靠代码审查和/或代码分析工具。