При создании более или менее сложных приложений для 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)
- 8 марта 2011 -- C#: Silverlight таймаут (Timeout) (2)
- 22 августа 2008 -- Собственная страница для обработки ошибок на ASP.NET (0)
- 15 сентября 2011 -- Еще раз о работе со службами (Windows Service) на C# (0)
- 19 сентября 2010 -- StringTemplate на C# (Часть 2) (0)


29 ноября, 2011 at 10:21
Купи учебник по русскому языку, безграмотный.
14 декабря, 2011 at 9:19
Почелуй меня в задницу, учитель хренов.
25 января, 2012 at 19:09
Привет. Благодарю за статью.. в каком-то смысле выручила.
) использовал BackgroundWorker. Может есть возможность используя BackgroundWorker избежать появления «The calling thread cannot access…»
Но к сожалению когда виполняеться метод отданный делегату то интерфейс замирает.. а этого хотелось избежать…
Я до этого (пока не возникало ошибок
Заранее брагодарен!