0

考虑 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 线程上处理要处理的事件?

提前致谢!

4

2 回答 2

2

有趣的是,您的代码对我有用。也许您遗漏了可以解释问题的部分代码。你能发布一个更完整的问题重现吗?modelChanged具体来说,除了将 lambda 分配给它之外,还要展示你对成员所做的事情。

我所做的是,创建一个空的 WPF 应用程序并从主窗口的构造函数运行您的 Init 方法。

然后我启动了直接调用modelChanged委托的后台线程。

我看到的是“ModelChanged on thread...”这一行总是打印正确的线程,即调用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();
    }
}
于 2011-10-18T22:21:01.633 回答
0

看起来你的情况Init()没有在 UI 线程上运行。为了确保某些东西在 UI 线程上运行,您可以使用一些控件的(例如Window's)Dispatcher属性,并使用它在 UI 线程上运行代码,如下所示:

someControl.Dispatcher.Invoke(() => { /* do something with the UI */ });

这个特定的重载Invoke()是一种扩展方法,需要引用System.Windows.Presentation.dllusing System.Windows.Threading;指令,并且需要 .NET 3.5 SP1 及更高版本。

于 2011-10-18T23:21:20.640 回答