Текущий SynchronizationContext не может использоваться как TaskScheduler

Я использую Tasks для запуска длинных вызовов сервера в моей модели ViewModel, и результаты снова сортируются на Dispatcher используя TaskScheduler.FromSyncronizationContext() . Например:

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

Это отлично работает при выполнении приложения. Но когда я запускаю свои тесты NUnit на Resharper я получаю сообщение об ошибке при вызове FromCurrentSynchronizationContext как:

Текущий SynchronizationContext не может использоваться как TaskScheduler.

Я думаю, это потому, что тесты выполняются на рабочих streamах. Как я могу обеспечить, чтобы тесты выполнялись в основном streamе? Любые другие предложения приветствуются.

Вам необходимо предоставить SynchronizationContext. Вот как я справляюсь с этим:

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

Решение Рича Мелтона не сработало для меня. Это связано с тем, что моя функция TestInitialize является асинхронной, как и мои тесты, поэтому каждый пользователь await что текущий SynchronizationContext будет потерян. Это связано с тем, что, как указывает MSDN, class SynchronizationContext является «глупым» и просто приостанавливает работу в пуле streamов.

То, что сработало для меня, фактически просто пропускает вызов FromCurrentSynchronizationContext когда нет SynchronizationContext (то есть, если текущий контекст является нулевым ). Если нет streamа пользовательского интерфейса, мне не нужно сначала синхронизировать его.

 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; } 

Я нашел это решение более простым, чем альтернативы, где:

  • TaskScheduler в ViewModel (через инъекцию зависимостей)
  • Создайте тестовый SynchronizationContext и «фальшивый» stream пользовательского интерфейса для тестов, которые будут выполняться на пути больше проблем для меня, что это стоит

Я теряю некоторый нюанс streamа, но я не проверяю, что мои вызовы OnPropertyChanged срабатывают по определенному streamу, поэтому я в порядке с этим. Другие ответы, использующие new SynchronizationContext() , в любом случае не делают ничего лучше для этой цели.

Я объединил несколько решений для гарантии работы 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 => { ... }); } 
  • В чем разница между задачей и streamом?
  • Как SynchronizationContext.Current основного streamа становится нулевым в приложении Windows Forms?
  • Async / ждет от BackgroundWorker
  • Есть ли что-то вроде асинхронного BlockingCollection ?
  • Как обрабатывать все необработанные исключения при использовании параллельной библиотеки задач?
  • Как отменить задачу в ожидании?
  • ASP.NET MVC4 Async controller - зачем использовать?
  • Как выполнить задачу на фоне wpf, когда вы можете предоставить отчет и разрешить аннулирование?
  • SynchronizationContext.Current имеет значение null в продолжении в основном streamе пользовательского интерфейса
  • HttpClient - задача была отменена?
  • Как отменить задачу ждут после периода ожидания
  • Давайте будем гением компьютера.