Получение TaskScheduler / SynchronizationContext для выполнения в определенном потоке - PullRequest
0 голосов
/ 19 октября 2011

Рассмотрим следующий код для 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 - обработчик событий для реагирования на изменения в объектной модели.Этот код выполняется в потоке пользовательского интерфейса и рассчитан на обработку событий в потоке пользовательского интерфейса независимо от того, из какого потока они запущены.

Однако, когда это запустится, результат будет примерно таким:

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);
}

... я вижу то же самое.

Что-то не так с моим мышлением или подходом?Как получить события для обработки в потоке инициализации?

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 19 октября 2011

Интересно, у меня твой код работает.Возможно, вы пропустили части кода, которые могут объяснить проблему.Можете ли вы опубликовать более полное воспроизведение проблемы?В частности, покажите, что вы делаете с элементом modelChanged, отличным от присвоения ему лямбды.

То, что я сделал, создайте пустое приложение WPF и запустите метод Init из конструктора главного окна.1004 *

Затем я запустил фоновые потоки, которые напрямую вызывали делегат 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();
    }
}
0 голосов
/ 19 октября 2011

Похоже, в вашем случае Init() не работает в потоке пользовательского интерфейса.Чтобы убедиться, что что-то выполняется в потоке пользовательского интерфейса, вы можете использовать свойство Dispatcher элемента управления (например, Window) и использовать его для запуска кода в потоке пользовательского интерфейса, например:Эта специфическая перегрузка Invoke() является методом расширения, который требует ссылки на System.Windows.Presentation.dll и директиву using System.Windows.Threading; и требует .NET 3.5 SP1 и выше.

...