是否可以测试方法是否是 运行 由任务启动时捕获的 SynchronizationContext?
Is it possible to test if a method is running with a captured SynchronizationContext when started by a Task?
我可能在某处遗漏了答案,或者是一些微不足道的东西,但我还没有找到。
这实际上是我试图在代码中完成的:
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
?
如果没有,是否有其他方法可以实现我的目标?
创建一个SynchronizationContext
,在Post
的实现中抛出异常并发送。或者,让它设置一个布尔值,指示 Send
或 Post
是否被调用,允许您稍后检查该布尔值(如果您这样做,您可能想要 运行 提供的委托,否则你可能会陷入僵局)。
每当测试不应使用当前同步上下文的方法时,在测试开始时将该自定义同步上下文的实例设置为当前同步上下文。
The goal is to weed out improper usages of ConfigureAwait in a very large code base.
一些团队为此选择使用代码分析工具。有几个可用。我见过的最常见的方法是 require 每个 await
一个 ConfigureAwait
,并明确指定 true
或 false
.这确保了每个 await
都经过审查并且上下文流是明确的。其他团队应用 "always use 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
是否会捕获上下文。请注意 "will"(将来时)- DoWhatever
完全有可能在甚至调用 ConfigureAwait(false)
之前运行并且 完成执行 。
现在,您可以从任务内部检查它是否运行在上下文现在。但在这种情况下,对于两个示例方法,DoWhatever
由于 Task.Run
将看不到上下文。因此,这并不能帮助您发现 CapturesContext
确实捕获了上下文这一事实; DoWhatever
看不到上下文,因此无法检测到它。
自定义 SynchronizationContext
是单元测试的一个很好的解决方案,但在运行时使用它会很尴尬,因为您确实有一些需要上下文的方法。出于这个原因,大多数团队选择依赖代码审查 and/or 代码分析工具。
我可能在某处遗漏了答案,或者是一些微不足道的东西,但我还没有找到。
这实际上是我试图在代码中完成的:
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
?
如果没有,是否有其他方法可以实现我的目标?
创建一个SynchronizationContext
,在Post
的实现中抛出异常并发送。或者,让它设置一个布尔值,指示 Send
或 Post
是否被调用,允许您稍后检查该布尔值(如果您这样做,您可能想要 运行 提供的委托,否则你可能会陷入僵局)。
每当测试不应使用当前同步上下文的方法时,在测试开始时将该自定义同步上下文的实例设置为当前同步上下文。
The goal is to weed out improper usages of ConfigureAwait in a very large code base.
一些团队为此选择使用代码分析工具。有几个可用。我见过的最常见的方法是 require 每个 await
一个 ConfigureAwait
,并明确指定 true
或 false
.这确保了每个 await
都经过审查并且上下文流是明确的。其他团队应用 "always use 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
是否会捕获上下文。请注意 "will"(将来时)- DoWhatever
完全有可能在甚至调用 ConfigureAwait(false)
之前运行并且 完成执行 。
现在,您可以从任务内部检查它是否运行在上下文现在。但在这种情况下,对于两个示例方法,DoWhatever
由于 Task.Run
将看不到上下文。因此,这并不能帮助您发现 CapturesContext
确实捕获了上下文这一事实; DoWhatever
看不到上下文,因此无法检测到它。
自定义 SynchronizationContext
是单元测试的一个很好的解决方案,但在运行时使用它会很尴尬,因为您确实有一些需要上下文的方法。出于这个原因,大多数团队选择依赖代码审查 and/or 代码分析工具。