当前的SynchronizationContext不能用作TaskScheduler

我正在使用Tasks在我的ViewModel中运行长时间运行的服务器调用,并使用TaskScheduler.FromSyncronizationContext()将结果整理回Dispatcher器。 例如:

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

这在我执行应用程序时正常工作。 但是当我在Resharper上运行我的NUnittesting时,我得到了FromCurrentSynchronizationContext调用的错误消息:

当前的SynchronizationContext不能用作TaskScheduler。

我想这是因为testing工作线程上运行。 我怎样才能确保testing在主线程上运行? 欢迎任何其他build议。

你需要提供一个SynchronizationContext。 这是我如何处理它:

 [SetUp] public void TestSetUp() { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); } 

里奇·梅尔顿的解决scheme并不适合我。 这是因为我的TestInitialize函数和我的testing一样是asynchronous的,所以每当await当前的SynchronizationContext丢失。 这是因为如MSDN指出的那样, SynchronizationContext类是“哑巴”,只是将所有的工作排队到线程池。

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

 TaskScheduler syncContextScheduler; if (SynchronizationContext.Current != null) { syncContextScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } else { // If there is no SyncContext for this thread (eg 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; } 

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

  • 将一个TaskScheduler传递给ViewModel(通过dependency injection)
  • 创build一个testingSynchronizationContext和一个“假”UI线程的testing运行 – 对我来说更麻烦,这是值得的

我失去了一些线程的细微差别,但我没有明确地testing我的OnPropertyChangedcallback在特定的线程触发,所以我没关系。 使用new SynchronizationContext()的其他答案实际上并没有为这个目标做更好的事情。

我已经结合了多个解决scheme来保证工作SynchronizationContext:

 using System; using System.Threading; using System.Threading.Tasks; public class CustomSynchronizationContext : SynchronizationContext { public override void Post(SendOrPostCallback action, object state) { SendOrPostCallback actionWrap = (object state2) => { SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext()); action.Invoke(state2); }; var callback = new WaitCallback(actionWrap.Invoke); ThreadPool.QueueUserWorkItem(callback, state); } public override SynchronizationContext CreateCopy() { return new CustomSynchronizationContext(); } public override void Send(SendOrPostCallback d, object state) { base.Send(d, state); } public override void OperationStarted() { base.OperationStarted(); } public override void OperationCompleted() { base.OperationCompleted(); } public static TaskScheduler GetSynchronizationContext() { TaskScheduler taskScheduler = null; try { taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } catch {} if (taskScheduler == null) { try { taskScheduler = TaskScheduler.Current; } catch {} } if (taskScheduler == null) { try { var context = new CustomSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } catch {} } return taskScheduler; } } 

用法:

 var context = CustomSynchronizationContext.GetSynchronizationContext(); if (context != null) { Task.Factory .StartNew(() => { ... }) .ContinueWith(x => { ... }, context); } else { Task.Factory .StartNew(() => { ... }) .ContinueWith(x => { ... }); }