Aktualna SynchronizationContext nie może być używana jako TaskScheduler
Używam Zadań do uruchamiania długich wywołań serwera w moim ViewModel, a wyniki są przenoszone z powrotem na Dispatcher
za pomocą TaskScheduler.FromSyncronizationContext()
. Na przykład:
var context = TaskScheduler.FromCurrentSynchronizationContext();
this.Message = "Loading...";
Task task = Task.Factory.StartNew(() => { ... })
.ContinueWith(x => this.Message = "Completed"
, context);
To działa dobrze, gdy uruchamiam aplikację. Ale kiedy uruchamiam moje testy {[3] } na Resharper
dostaję komunikat o błędzie podczas połączenia do FromCurrentSynchronizationContext
jako:
Bieżący SynchronizationContext nie może być używany jako TaskScheduler.
Myślę, że to dlatego, że testy są uruchamiane na wątkach roboczych. Jak mogę zapewnić testy są uruchamiane na głównym wątku ? Wszelkie inne sugestie są mile widziane.
3 answers
Musisz podać SynchronizationContext. Tak sobie z tym radzę:
[SetUp]
public void TestSetUp()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-23 16:55:25
Rozwiązanie Ritcha Meltona nie zadziałało. Dzieje się tak dlatego, że moja TestInitialize
funkcja jest asynchroniczna, podobnie jak moje testy, więc z każdym await
prąd SynchronizationContext
jest tracony. Dzieje się tak dlatego, że jak podkreśla MSDN, klasa SynchronizationContext
jest "głupia" i po prostu kolejkuje całą pracę do puli wątków.
To, co dla mnie działało, to po prostu pomijanie wywołania FromCurrentSynchronizationContext
, gdy nie ma SynchronizationContext
(to znaczy, jeśli bieżący kontekst to null ). Jeśli nie ma wątku UI, nie muszę z nim synchronizować w pierwsze miejsce.
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;
}
Znalazłem to rozwiązanie prostsze niż alternatywy, które gdzie:
- W ten sposób można uzyskać więcej informacji na temat tego, co się dzieje w ViewModel.]}
W tym celu należy utworzyć nowy wątek, który pozwoli na uruchomienie testów w przyszłości, co będzie dla mnie bardziej kłopotliwe, niż jest to dla mnie warte.]}
Tracę niektóre niuanse wątków, ale nie testuję wyraźnie, że moje wywołania zwrotne OnPropertyChanged wyzwalają konkretny wątek, więc mi to nie przeszkadza. Inne odpowiedzi za pomocą new SynchronizationContext()
i tak nie robią nic lepszego dla tego celu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 23:42:28
Połączyłem wiele rozwiązań, aby mieć gwarancję pracy 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;
}
}
Użycie:
var context = CustomSynchronizationContext.GetSynchronizationContext();
if (context != null)
{
Task.Factory
.StartNew(() => { ... })
.ContinueWith(x => { ... }, context);
}
else
{
Task.Factory
.StartNew(() => { ... })
.ContinueWith(x => { ... });
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-08 14:54:22