Текущий SynchronizationContext не может использоваться в качестве TaskScheduler
Я использую задачи для запуска длительных вызовов сервера в моем 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.
Я думаю, это потому, что тесты выполняются в рабочих потоках. Как я могу гарантировать, что тесты выполняются в основном потоке ? Любые другие предложения приветствуются.
3 ответов:
вам нужно предоставить SynchronizationContext. Вот как я справляюсь с этим:
[SetUp] public void TestSetUp() { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); }
решение Рича Мелтона не сработало для меня. Это потому, что мой
TestInitializeфункция асинхронна, как и мои тесты, поэтому с каждымawaitтекущегоSynchronizationContextтеряется. Это потому, что, как указывает MSDN,SynchronizationContextкласс "тупой" и просто ставит все работы в очередь в пул потоков.то, что сработало для меня, на самом деле просто пропускает
FromCurrentSynchronizationContextзвоните, когда нетSynchronizationContext(то есть, если текущий контекст null). Если нет потока пользовательского интерфейса, мне не нужно синхронизировать с ним в первую очередь.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; }я нашел это решение более простым, чем альтернативные варианты, где:
- Pass a
TaskSchedulerв ViewModel (через инъекцию зависимостей)- создать тест
SynchronizationContextи "поддельный" поток пользовательского интерфейса для тестов, чтобы запустить на пути больше проблем для меня, что это стоитя теряю некоторые нюансы резьбы, но я явно не тестирую, что мой OnPropertyChanged обратные вызовы запускаются в определенном потоке, поэтому я в порядке с этим. Другие ответы с помощью
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 => { ... }); }
Comments