当前的SynchronizationContext可能不会用作TaskScheduler

ani*_*vas 96 c# multithreading nunit task-parallel-library resharper-6.0

我正在使用Tasks在我的ViewModel中运行长时间运行的服务器调用,结果将被重新编组Dispatcher使用TaskScheduler.FromSyncronizationContext().例如:

var context = TaskScheduler.FromCurrentSynchronizationContext();
this.Message = "Loading...";
Task task = Task.Factory.StartNew(() => { ... })
            .ContinueWith(x => this.Message = "Completed"
                          , context);
Run Code Online (Sandbox Code Playgroud)

这在我执行应用程序时工作正常.但是当我运行我的NUnit测试时,Resharper我在调用时收到错误消息FromCurrentSynchronizationContext:

当前的SynchronizationContext可能不会用作TaskScheduler.

我想这是因为测试是在工作线程上运行的.如何确保测试在主线程上运行?欢迎任何其他建议.

Rit*_*ton 139

您需要提供SynchronizationContext.这就是我处理它的方式:

[SetUp]
public void TestSetUp()
{
  SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
Run Code Online (Sandbox Code Playgroud)

  • 对于MSTest:将上面的代码放在标有ClassInitializeAttribute的Method中. (6认同)
  • @SACO:实际上,我必须把它放在一个带有`TestInitializeAttribute`的方法中,否则只有第一个测试通过. (5认同)
  • 我完全不明白为什么这个答案被接受为解决方案.这是行不通的.原因很简单:SynchronizationContext是一个虚拟类,其发送/发布功能无用.这个类应该是抽象的而不是具体的类,可能会导致人们误解"它正在工作".@tofutim您可能希望提供从SyncContext派生的自己的实现. (3认同)
  • 对于xunit测试,我把它放在静态类型ctor中,因为它只需要在每个灯具上设置一次. (2认同)

Sap*_*pph 21

Ritch Melton的解决方案对我不起作用.这是因为我的TestInitialize函数是异步的,就像我的测试一样,因此每await一个电流SynchronizationContext都会丢失.这是因为MSDN指出,SynchronizationContext该类是"哑",只是将所有工作排队到线程池.

对我来说有用的实际上就是在FromCurrentSynchronizationContext没有a时跳过调用SynchronizationContext(也就是说,如果当前上下文为null).如果没有UI线程,我首先不需要与它同步.

TaskScheduler syncContextScheduler;
if (SynchronizationContext.Current != null)
{
    syncContextScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
else
{
    // If there is no SyncContext for this thread (e.g. we are in a unit test
    // or console scenario instead of running in an app), then just use the
    // default scheduler because there is no UI thread to sync with.
    syncContextScheduler = TaskScheduler.Current;
}
Run Code Online (Sandbox Code Playgroud)

我发现这个解决方案比替代方案更直接,其中:

  • 传递TaskScheduler给ViewModel(通过依赖注入)
  • 创建一个测试SynchronizationContext和一个"虚假的"UI线程,让测试运行起来 - 这对我来说更麻烦,值得

我失去了一些线程细微差别,但我没有明确测试我的OnPropertyChanged回调在特定线程上触发,所以我没关系.new SynchronizationContext()无论如何,使用其他答案对于该目标并没有做得更好.