考虑WPF ViewModel的以下代码:
protected void Init()
{
Debug.WriteLine(string.Format("ChangeManager init on thread={0}", Thread.CurrentThread.ManagedThreadId));
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
this.modelChanged = (o, args) => Task.Factory.StartNew(() =>
{
Debug.WriteLine(string.Format("ModelChanged on thread={0}", Thread.CurrentThread.ManagedThreadId));
this.ModelChanged(o, args);
},
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
}
...其中modelChanged是用于响应对象模型中的更改的事件处理程序。此代码在UI线程上执行,其设计希望在UI线程上处理事件,而不管它们从哪个线程触发。
然而,当它运行时,输出类似于:
ChangeManager init on thread=1
ModelChanged on thread=3
ModelChanged on thread=3
ModelChanged on thread=7
ModelChanged on thread=9
我的期望是线程1将是所有处理将发生的地方。即使我尝试直接使用SynchronizationContext,如下所示:
protected void Init()
{
Debug.WriteLine(string.Format("ChangeManager init on thread={0}", Thread.CurrentThread.ManagedThreadId));
this.uiContext = SynchronizationContext.Current;
modelChanged = (o, args) => uiContext.Post((ignore) => {
Debug.WriteLine(string.Format("ModelChanged on thread={0}", Thread.CurrentThread.ManagedThreadId));
this.ModelChanged(o, args);
}
, null);
}
......我看到同样的事情。
我的想法或方法有问题吗?如何在init线程上处理事件?
提前致谢!
答案 0 :(得分:2)
有趣的是,您的代码适合我。也许您遗漏了可以解释问题的部分代码。你能发布一个更完整的问题再现吗?具体而言,除了将lambda分配给它之外,还要显示您使用modelChanged
成员所做的事情。
我做的是,创建一个空的WPF应用程序并从主窗口的构造函数中运行Init方法。
然后我开始直接调用modelChanged
委托的后台线程。
我看到的是“线程上的ModelChanged ......”行总是打印出正确的线程,即调用Init
的线程。
如果有任何帮助,这就是我尝试重现它的方法,您可以查看它,也许可以发布您正在做的不同的事情:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Init();
}
private EventHandler modelChanged;
protected void Init()
{
Trace.WriteLine(string.Format("ChangeManager init on thread={0}",
Thread.CurrentThread.ManagedThreadId));
var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
modelChanged = (o, args) => Task.Factory.StartNew(() =>
{
Trace.WriteLine(string.Format("ModelChanged on thread={0}",
Thread.CurrentThread.ManagedThreadId));
if (ModelChanged != null)
{
ModelChanged(o, args);
}
},
CancellationToken.None,
TaskCreationOptions.None,
uiTaskScheduler);
}
public event EventHandler ModelChanged;
private void button1_Click(object sender, RoutedEventArgs e)
{
var t = new Thread(
obj =>
{
Trace.WriteLine(string.Format(
"Launching handler on thread={0}",
Thread.CurrentThread.ManagedThreadId));
modelChanged(null, EventArgs.Empty);
});
t.Start();
}
}
答案 1 :(得分:0)
在您的情况下看起来Init()
未在UI线程上运行。要确保某些内容在UI线程上运行,您可以使用某些控件(例如Window
')Dispatcher
属性,并使用它在UI线程上运行代码,如下所示:
someControl.Dispatcher.Invoke(() => { /* do something with the UI */ });
Invoke()
的特定重载是一种扩展方法,需要引用System.Windows.Presentation.dll
和using System.Windows.Threading;
指令,并且需要.NET 3.5 SP1及更高版本。