При создании более или менее сложных приложений для 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).
Вот пример решения для 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");
}
}
Метки:C#, Windows, разработка
Похожие статьи
- 6 февраля 2009 -- Загрузка Flash в Windows.Forms и WPF. (2)
- 22 августа 2008 -- Собственная страница для обработки ошибок на ASP.NET (0)
- 30 декабря 2008 -- Создаем ASHX хендлер в ASP.NET (1)
- 19 июня 2008 -- IIS 6 и ASP.NET MVC. (2)
- 30 июля 2010 -- DLR 1.0 на примере IronPython 2.6 (0)



Ваше слово: