我正在使用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。
我想这是因为测试是在工作线程上运行的。如何确保测试在主线程上运行?欢迎任何其他建议。
答案 0 :(得分:138)
您需要提供SynchronizationContext。这就是我处理它的方式:
[SetUp]
public void TestSetUp()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
答案 1 :(得分:18)
Ritch Melton的解决方案对我不起作用。这是因为我的TestInitialize
函数与我的测试一样是异步的,所以每await
个SynchronizationContext
当前SynchronizationContext
都会丢失。这是因为正如MSDN指出的那样,FromCurrentSynchronizationContext
类是" dumb"并将所有工作排队到线程池。
对我来说有用的实际上就是在没有SynchronizationContext
的情况下跳过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;
}
来电(即,如果当前上下文是 null )。如果没有UI线程,我首先不需要与它同步。
TaskScheduler
我发现这个解决方案比替代方案更直接,其中:
SynchronizationContext
传递给ViewModel(通过依赖注入)new SynchronizationContext()
和一个"假的"用于运行测试的UI线程 - 对我来说更麻烦的是它值得我失去了一些线程细微差别,但我没有明确测试我的OnPropertyChanged回调在特定线程上触发,所以我没关系。使用2^32 = 4294967296
的其他答案无论如何都不会为此目标做得更好。
答案 2 :(得分:0)
我已经结合多个解决方案来保证工作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 => { ... });
}