C# 为什么Dispatcher.Invoke在本例中不执行委托参数?
这段代码不会引发异常。同时,Dispatcher.Invoke不执行任何操作。我觉得很奇怪 我将一个助手方法提取到一个基本ViewModel中。工作线程使用此方法DoOnUIThread()来避免线程关联问题。 然而,在我的单元测试中,我发现由于上述问题,尝试测试视图模型对象会导致失败C# 为什么Dispatcher.Invoke在本例中不执行委托参数?,c#,.net,wpf,unit-testing,C#,.net,Wpf,Unit Testing,这段代码不会引发异常。同时,Dispatcher.Invoke不执行任何操作。我觉得很奇怪 我将一个助手方法提取到一个基本ViewModel中。工作线程使用此方法DoOnUIThread()来避免线程关联问题。 然而,在我的单元测试中,我发现由于上述问题,尝试测试视图模型对象会导致失败 我可以将整个行为转移到一个可插入的依赖项中,我可以在测试中替换它。e、 ViewModelBase依赖于UIThreadExecutor.Execute(Action),我在测试中使用了一个只调用该操作的伪函数。
我可以将整个行为转移到一个可插入的依赖项中,我可以在测试中替换它。e、 ViewModelBase依赖于UIThreadExecutor.Execute(Action),我在测试中使用了一个只调用该操作的伪函数。但是,我很好奇为什么Dispatcher的行为如此..看起来您只是将其作为参数传递给
BackgroundWorker
。尝试将此添加到SomeWork
函数中:
[Test]
public void A()
{
var d = Dispatcher.CurrentDispatcher;
Action action = () => Console.WriteLine("Dispatcher invoked me!");
var worker = new BackgroundWorker();
worker.DoWork += SomeWork;
//worker.RunWorkerAsync( (Action) delegate { Console.WriteLine("This works!"); } );
worker.RunWorkerAsync((Action) delegate { d.Invoke(action); } );
System.Threading.Thread.Sleep(2500);
}
private void SomeWork(object sender, DoWorkEventArgs e)
{
(e.Argument as Action)();
}
您可以尝试以下方法,而不是直接的睡眠:
public void SomeWork(object sender, DoWorkEventArgs e)
{
((MulticastDelegate)e.Argument).DynamicInvoke();
}
看起来您只是将其作为参数传递给BackgroundWorker
。尝试将此添加到SomeWork
函数中:
[Test]
public void A()
{
var d = Dispatcher.CurrentDispatcher;
Action action = () => Console.WriteLine("Dispatcher invoked me!");
var worker = new BackgroundWorker();
worker.DoWork += SomeWork;
//worker.RunWorkerAsync( (Action) delegate { Console.WriteLine("This works!"); } );
worker.RunWorkerAsync((Action) delegate { d.Invoke(action); } );
System.Threading.Thread.Sleep(2500);
}
private void SomeWork(object sender, DoWorkEventArgs e)
{
(e.Argument as Action)();
}
您可以尝试以下方法,而不是直接的睡眠:
public void SomeWork(object sender, DoWorkEventArgs e)
{
((MulticastDelegate)e.Argument).DynamicInvoke();
}
当主线程空闲并重新进入分派循环时,分派器只能执行其Begin/Invoke()职责。此时,主线程处于静止状态,可以安全地执行已调度的请求。以及Windows发送给它的任何通知
在您的情况下,它不是空闲的,它被困在睡眠(2500)中。只有当主线程空闲并重新进入调度循环时,调度程序才能执行其Begin/Invoke()任务。此时,主线程处于静止状态,可以安全地执行已调度的请求。以及Windows发送给它的任何通知
在你的情况下,它不是空闲的,它被困在睡眠中(2500)。这是我使用的,它对我有用,YMMV:
while (worker.IsBusy)
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new EmptyDelegate(delegate{}));
//Application.DoEvents();
System.Threading.Thread.Sleep(10);
}
注意事项:
- 在VS 2012的.NET 4.0控制台应用程序中进行了测试——具体取决于您的单元测试运行程序YMMV
- 在DoEvents扩展方法中,您可以注释掉一个或另一个try/catch块(即,仅使用WPF方式,或仅使用WinForms方式)——它仍然可以工作——我希望两者都可以使用,以防万一——如果你想用WinForms的方式做这件事:你需要在你的项目中添加对System.Windows.Forms的引用
- Thread.Yield/Thread.Sleep不是必需的,并且不会为解决问题增加价值(Sleep和Yield都不会运行任何排队的调度程序事件)——但它们会减少该线程上的CPU使用量(即,笔记本电脑电池寿命更长,CPU风扇更安静,等等)如果你只是在一个永远忙碌的循环中等待,那么在windows上玩得会更好。它还将增加一些开销,因为这是本来可以用来运行排队调度程序事件的时间
- 调用
dispatcher.Invoke
,从与dispatcher相同的线程调用,似乎只是直接调用方法,即没有理由调用dispatcher.DoEvents()
——但是,从同一线程调用dispatcher.BeginInvoke
,将不会立即执行调用,它将等待您调用dispatcher.DoEvents()
这是我使用的,它对我有用,YMMV:
while (worker.IsBusy)
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background,new EmptyDelegate(delegate{}));
//Application.DoEvents();
System.Threading.Thread.Sleep(10);
}
注意事项:
- 在VS 2012的.NET 4.0控制台应用程序中进行了测试——具体取决于您的单元测试运行程序YMMV
- 在DoEvents扩展方法中,您可以注释掉一个或另一个try/catch块(即,仅使用WPF方式,或仅使用WinForms方式)——它仍然可以工作——我希望两者都可以使用,以防万一——如果你想用WinForms的方式做这件事:你需要在你的项目中添加对System.Windows.Forms的引用
- Thread.Yield/Thread.Sleep不是必需的,并且不会为解决问题增加价值(Sleep和Yield都不会运行任何排队的调度程序事件)——但它们会减少该线程上的CPU使用量(即,笔记本电脑电池寿命更长,CPU风扇更安静,等等)如果你只是在一个永远忙碌的循环中等待,那么在windows上玩得会更好。它还将增加一些开销,因为这是本来可以用来运行排队调度程序事件的时间
- 调用
dispatcher.Invoke
,从与dispatcher相同的线程调用,似乎只是直接调用方法,即没有理由调用dispatcher.DoEvents()
——但是,从同一线程调用dispatcher.BeginInvoke
,将不会立即执行调用,它将等待您调用dispatcher.DoEvents()
那么,有没有一种方法可以启动单元测试的消息循环—Wpf EXE的Application.Run()的对应项?调用不是应该是同步/立即的吗?@Gishu-您可以循环BackgroundWorker的IsBusy属性。这不是一个理想的解决方案,但应该可行。请参阅“我的编辑”。@SwDev-如果BGW有RunWorkerCompleted事件,这将是一个保证死锁。在事件运行之前,它无法停止IsBusy,而在循环中测试IsBusy时,事件无法运行。@Gishu:为什么不使用Application.run?是的,它与UI线程同步。不,它不是即时的,因为UI线程必须是空闲的。这对于ViewModel对象的单元测试来说是一个很大的负担,它与UI无关。此外,App.Run()还会阻止测试运行程序线程。我想我必须将这种行为转移到依赖项上。那么,有没有一种方法可以启动单元测试的消息循环——Wpf EXE的Application.Run()的对应项?调用不是应该是同步/立即的吗?@Gishu-您可以循环BackgroundWorker的IsBusy属性。这不是一个理想的解决方案,但应该可行。请参阅“我的编辑”。@SwDev-如果BGW有RunWorkerCompleted事件,这将是一个保证死锁。在事件运行之前,它无法停止处于忙碌状态。当您处于循环中时,事件无法运行