- Хроники. - -

Мультипоточность в Windows.Forms и WPF

Posted By Ikutsin On 12 февраля 2009 @ 10:45 In .NET C# | Comments Disabled

При создании более или менее сложных приложений для Windows. Неизбежно возникает проблема организации доступа к данным из разных потоков. В Windows.Forms это выглядит так:
Cross-thread operation not valid: Control ‘textBox1’ accessed from a thread other than the thread it was created on.
В WPF это выглядит так:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
В обеих технологиях есть разные, но простые способы это решить.

Windows Forms.

Следующий код выкинет исключение:

namespace TestMultithreading
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            (new Action(TestThreading)).BeginInvoke(null, null); //Async. delegate call.
            textBox1.Text = "Same thread";
        }
        void TestThreading()
        {
            Thread.Sleep(100);
            textBox1.Text = "Async change"; //Exception here.
        }
    }
}

Для решения проблемы, нужно проверить, действительно ли доступ до формы пытается получить другой процесс. Создать поток в контексте формы.
Для этого, можно создать форме новый потоко-независимый метод. Мой называется AddText. Модифицированный класс выглядит следующим образом:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        (new Action(TestThreading)).BeginInvoke(null, null);
        textBox1.Text = "Same thread";
    }
    void TestThreading()
    {
        Thread.Sleep(1000);
        AddText("Async change");
    }

    public void AddText(string text)
    {
        if (this.textBox1.InvokeRequired)
        {
            Action updaterdelegate = new Action(AddText);
            try
            {
                this.Invoke(updaterdelegate, new object[] { text });
            }
            catch (ObjectDisposedException ex) { }
        }
        else
        {
            textBox1.Text = text;
        }
    }
}

После вызова AddText происходит проверка InvokeRequired, если сребуется Invoke, то он делается через делегат со значением того-же метода.

WPF

В WPF получить исключение можно тем-же образом как и в Windows.Forms:

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            (new Action(TestThreading)).BeginInvoke(null, null);
        }
        void TestThreading()
        {
            textBox1.Text = "Async call"; //
        }
    }
}

Для решения задачи не требуется проверок. Дело в том, что все изменения контролируются так называемым Dispatcher-ом. Он инкапсулирован в класс
DispatcherObject, который в свою очередь является родителем DependencyObject, UIElement и соответственно всех визуальных элементов WPF. Раскрытие всех тонкостей Диспечера, задача не этой статьи. Для начала, достаточно разобраться с приоритетами (DispatcherPriority) [1].
Вот пример решения для WPF:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        (new Action(TestThreading)).BeginInvoke(null, null);
    }
    void TestThreading()
    {
        Thread.Sleep(1000);
        Dispatcher.BeginInvoke(new Action(s =>
        {
            textBox1.Text = s;
        }), DispatcherPriority.Render, "AsyncCall");
        
    }
}

Article printed from Хроники.:

URL to article: /792-multipotochnost-v-windowsforms-i-wpf

URLs in this post:

[1] приоритетами (DispatcherPriority): http://msdn.microsoft.com/ru-ru/library/system.windows.threading.dispatcherpriority.aspx

Copyright © 2008 Все, что меня окружает. All rights reserved.