Ситуация следующая:
button1
и меткой label1
.label1
.При попытке поменять значение label1.Text
код падает с исключением InvalidOperationException
:
WinForms:
Cross-thread operation not valid: Control 'label1' accessed from a thread other than the thread it was created on.
Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'label1' не из того потока, в котором он был создан.
WPF:
The calling thread cannot access this object because a different thread owns it.
Вызывающий поток не может получить доступ к данному объекту, так как владельцем этого объекта является другой поток.
Пример кода:
private void button1_Click(object sender, EventArgs e)
{
(new Thread((s) =>
{
var result = Worker.SomeLongOperation();
// следующая строчка падает c InvalidOperationException:
this.label1.Text = result;
})).Start();
}
class Worker
{
public static string SomeLongOperation()
{
Thread.Sleep(1000);
return "результат";
}
}
Использовать Асинхронную модель на основе задач (TAP) и ключевые слова async-await:
private async void button1_Click(object sender, EventArgs e)
{
string result = await Task.Factory.StartNew<string>(
() => Worker.SomeLongOperation(),
TaskCreationOptions.LongRunning);
this.label1.Text = result;
}
Преимущества:
Встроенная поддержка ключевых слова async
/await
появились в .NET 4.5 и Visual Studio 2013.
Данное решение также может быть использовано для .NET 4.0 и Silverlight 5, если используется версия Visual Studio не ниже 2012. Для этого нужно установить пакет Microsoft.Bcl.Async из NuGet.
Решение с отображением прогресса выполненияЕсли в процессе выполнения нужно отображать прогресс или промежуточные результаты из второго потока, то можно использовать класс Progress:
private async void button1_Click(object sender, EventArgs e)
{
var progress = new Progress<string>(s => label1.Text = s);
string result = await Task.Factory.StartNew<string>(
() => Worker.SomeLongOperation(progress),
TaskCreationOptions.LongRunning);
this.label1.Text = result;
}
class Worker
{
public static string SomeLongOperation(IProgress<string> progress)
{
// Perform a long running work...
for (var i = 0; i < 10; i++)
{
Task.Delay(500).Wait();
progress.Report(i.ToString());
}
return "результат";
}
}
Progress
захватывает SynchronizationContext в момент создания, и использует его для выполнения операций, избавляя от ручных вызовов Invoke.
Использовать Invoke
/BeginInvoke
:
// WinForms:
this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result));
// WPF:
Dispatcher.BeginInvoke((Action)(() => this.label1.Content = result));
Для .NET 2.0, в котором еще не было лямбд, эквивалентный код записывается с помощью анонимных делегатов:
// WinForms:
this.label1.BeginInvoke((MethodInvoker)(delegate { this.label1.Text = result; });
// WPF:
Dispatcher.BeginInvoke((Action)(delegate { this.label1.Content = result; });
Полный код:
private void button1_Click(object sender, EventArgs e)
{
(new Thread((s) =>
{
var result = Worker.SomeLongOperation();
this.label1.BeginInvoke((MethodInvoker)(() => this.label1.Text = result));
})).Start();
}
BeginInvoke
поставит код на выполнение в тот поток, в котором был создан label1
и продолжит выполнение фонового потока. При использовании Invoke
вместо BeginInvoke
фоновый поток будет приостановлен до завершения выполнения кода в UI потоке.
Еще есть пример для UI для безопасного вызова из не главного потока.
Чтобы взаимодействовать с элементами интерфейса из другого thread
void SomeAsyncMethod()
{
// Do some work
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)(() =>
{
DoUpdateUI();
}
));
}
else
{
DoUpdateUI();
}
}
void DoUpdateUI()
{
// Your UI update code here
}
Здесь this.Invoke метод контрола
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Допустим, есть класс MyClass<T>В этом классе есть метод Common()
подскажите пожалуйста, как собрать сборку Chromium с видео кодекам? интересует воспроизведение видео в программе где будет использоваться Chromiumнужен...
друзьяРазрабатываю масштабный проект на c# WinForms, столкнулся с проблемой утечки памяти