我知道在这个网站和其他网站上有几个类似的问题,但是出于某种原因,这种做法的标准方式似乎不适用于我的情况。完成此要求的正常方法是使用TaskScheduler.FromCurrentSynchronizationContext()
作为相关Task.Factory.StartNew
overload中的TaskScheduler
输入参数:
// Set uiTaskScheduler whilst on the UI thread
TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
...
Task.Factory.StartNew(() => SomeMethodToRunAsynchronously(),
CancellationToken.None, TaskCreationOptions.None, uiTaskScheduler);
这似乎足以安排Task
在UI线程上运行,但它似乎在我的情况下不起作用。就我而言,我有一个UiThreadManager
类,其中包含RunAsynchronously
方法:
public Task RunAsynchronously(Action method)
{
return Task.Run(method);
}
这部分工作正常。我面临的问题是,在运行单元测试时,这个类被替换为MockUiThreadManager
类(都实现了应用程序代码使用的IUiThreadManager
接口),我似乎无法强迫此方法在UI线程上运行:
public Task RunAsynchronously(Action method)
{
return Task.Factory.StartNew(() => method(),
CancellationToken.None, TaskCreationOptions.None, UiTaskScheduler);
}
MockUiThreadManager
类在UI线程上设置了static UiTaskScheduler
属性(如下所示),所以我假设通过上述方法的所有代码都会按预期在该线程上运行:< / p>
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
MockUiThreadManager.UiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
但是,在运行单元测试时,我注意到它在之前完成了正在测试的代码。因此,我在每个断点处的Visual Studio立即窗口中添加了一些断点并调用System.Threading.Thread.CurrentThread.ManagedThreadId
,当然,当代码通过上述方法时,线程ID也发生了变化。
基本上,我正在寻找一种方法来伪基于Task
的异步调用,用于在单元测试期间运行的RunAsynchronously
方法,以确保方法实际上在UI线程上同步运行。有人看到我做错了什么,还是有其他建议?
更新&gt;&gt;&gt;
好的,我在这里使用了关于UI线程的错误术语。单元测试不在UI线程上运行,因为没有UI ...但是,情况保持不变。为了澄清,我只需要我的测试同步运行应用程序代码,并在它启动的 main 单线程上运行。问题是当应用程序运行时,有很多基于Task
的异步代码,需要同步通过MockUiThreadManager
类进行测试。
答案 0 :(得分:4)
继续我的搜索后,我现在找到了我正在寻找的解决方案,只需几行代码即可实现,所以绝对不需要实现我自己的SynchronizationContext
类。看看它有多简单,我很惊讶我之前没有找到它。在我运行单元测试时使用的MockUiThreadManager
类中,我现在有了这段代码:
public Task RunAsynchronously(Action method)
{
Task task = new Task(method);
task.RunSynchronously();
return task;
}
我可以确认它是按照它说的做的,并在运行测试的同一个线程上同步运行method
函数。
为了完整起见,Task.RunSynchronously
Method还有一个覆盖TaskScheduler
的覆盖,但在我的情况下这是不必要的,所以我不再需要MockUiThreadManager.UiTaskScheduler
属性。< / p>
答案 1 :(得分:2)
new SynchronizationContext()
返回一个新的默认SynchronizationObject,用于调度线程池(reference source)上的工作。
TaskScheduler.FromCurrentSynchronizationContext()
返回使用SynchronizationContext.Current
的任务计划程序,您只需使用SynchronizationContext.SetSynchronizationContext
将其设置为线程池同步上下文。
这意味着在该调度程序上的调度任务将使用线程池来执行它们,而不是特定的线程。
通常,甚至无法在特定线程上安排工作,除非该线程具有某种类型的消息队列。这就是为什么你可以安排在UI线程上运行的工作。
我不知道你使用哪个单元测试框架。它可能有一个UI来显示测试结果,但这并不意味着测试是在该线程上运行的。
除了编写自己的SynchronizationContext
课程之外,我不知道如何解决这个问题。你想要对必须在UI线程上运行的东西进行单元测试也感觉有点奇怪,因为在我看来,UI本身是不适合单元测试的东西。如果您使用ViewModel
或Controller
之类的内容,那么您当然可以对这些内容进行单元测试,但无论您使用哪种同步上下文,它们都应该有效。