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

C# 当由任务启动时,是否可以测试方法是否正在使用捕获的SynchronizationContext运行?,c#,task,synchronizationcontext,.net-4.7.2,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(f

我可能在某个地方遗漏了答案,或者是一些琐碎的东西,但我还没有找到

以下是我试图在代码中实现的目标:

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?
}
上面的例子过于简化,但表达了我希望实现的目标

目标是在非常大的代码库中消除ConfigureWait的不当使用

给定使用任务运行的任何方法,是否可以通过诸如断言或单元测试之类的代码检查给定方法是否使用捕获的SynchronizationContext运行

如果没有,是否有其他方法可以实现我的目标?

创建一个SynchronizationContext,在Post和Send的实现中引发异常。或者,让它设置一个布尔值,指示是否调用了Send或Post,允许您稍后检查该布尔值。如果执行此操作,您可能希望运行提供的委托,否则可能会出现死锁

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

目标是在非常大的代码库中消除ConfigureWait的不当使用

一些团队为此选择使用代码分析工具。有几种方法可供选择。我见过的最常见的方法是为每个等待都要求一个ConfigureAwait,并显式指定true或false。这确保了每个等待都经过了审查,并且上下文流是明确的。其他团队应用特定于项目的规则always use ConfigureWaitFalse,对于无法遵循该规则的项目,只依赖于代码审查

示例代码的问题是,由于Task.Run,Dowhat永远无法知道它是否被间接调用。如果重写这些方法,这一点就会变得很清楚:

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;
}
重写方法的第一行应该清楚地表明,Dow不知道CapturesContext或DoesNotCaptureContext是否会捕获上下文。注意will将来时态-完全有可能在调用configurewaitfalse之前运行并完成执行

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


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

我能想象的不正确使用ConfigureAwaitfalse的唯一情况是当您需要更新UI上的某些内容时,否则我认为ConfigureAwaitfalse是避免不必要地更改线程的一个好做法,还可以考虑控制台应用程序和.NET内核,也许还有其他一些没有同步上下文。@未命名的我不使用.NETCar更新了标签。很多时候我不想使用ConfigureWaitFalse,例如在ASP.NET、WinForms、WPF线程中,以及当我需要当前区域性进行本地化时。除其他原因外,+1这适用于单元测试,这很有帮助。我是否可以在不使用自定义SynchronizationContext的情况下执行编译时甚至运行时抛出异常检查?@Zer0任何编译时检查都只是猜测。您永远无法知道是否有任何东西可以使用当前的同步上下文,因为没有足够的可用信息从代码分析工具的角度确定,并且您需要进行的流诊断在一般情况下是无法解决的问题,所以你会有一些误报和/或误报。编写一个定制的同步上下文如此简单快捷,我不明白你为什么要避免它。如果可能的话,避免它将是更多的工作。是的,我怀疑编译时是可能的,但可能是运行时?我想说的是,是否有一种不用单元测试的检查方法。我确实同意这个解决方案是简单的,并且符合条例草案,假设适用的单元测试已经到位。如果某些东西没有经过单元测试,我想知道我是否可以在方法本身中快速失败。@Zer0当然可以。在单元测试中不需要这样做。您可以在单元测试之外设置同步上下文。假设在这种情况下,您只需将其设置为默认上下文,而不是抛出的上下文。如果您想这样做,您甚至不需要避免在方法中捕获上下文。如果这样做,在将控件返回给调用方时,不要忘记将原始上下文设置回原位。