Во время создания простого примера с использованием async/await
я обнаружил, что некоторые примеры просто иллюстрируют шаблон на Button1_Click
как методы и свободно обновляют элементы управления графическим интерфейсом непосредственно из методов async
. Поэтому можно считать это безопасным механизмом. Но мой тестовый код постоянно рушился на TargetInvocationException
исключениях в mscorlib.dll
с внутренними исключениями, такими как: , ArgumentOutOfRange
и т. Д. Что касается трассировки стека, все, казалось, указывало на метки, отображающие результаты (и управляемые непосредственно из методов async
, привязанных к кнопке обработчики событий). Сбой, по-видимому, исправляется при использовании старой школы Control.Invoke
при доступе к элементам управления графическим интерфейсом.Обновление GUI из метода async
Вопросы: Я пропустил что-то важное? Используются ли методы асинхронизации так же, как и работники нитей/фона, ранее использовавшиеся для долгосрочных операций, и поэтому рекомендуемое решение Invoke
? Являются ли фрагменты кода, управляющие графическим интерфейсом напрямую с async
, неправильными?
EDIT: Для недостающего исходного downvoters: создать простую форму, содержащую три кнопки и один StatusStrip, содержащие две метки ...
//#define OLDSCHOOL_INVOKE
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AsyncTests
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void LongTermOp()
{
int delay;
int thisId;
lock (mtx1)
{
delay = rnd.Next(2000, 10000);
thisId = firstCount++;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label1Gen.Text = $"Generating first run delay #{thisId} of {delay} ms"
#if OLDSCHOOL_INVOKE
))
#endif
;
++firstPending;
}
await Task.Delay(delay);
lock (mtx1)
{
--firstPending;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label1Gen.Text = $"First run #{thisId} completed, {firstPending} pending..."
#if OLDSCHOOL_INVOKE
))
#endif
;
}
}
private async Task LongTermOpAsync()
{
await Task.Run((Action)LongTermOp);
}
private readonly Random rnd = new Random();
private readonly object mtx1 = new object();
private readonly object mtx2 = new object();
private int firstCount;
private int firstPending;
private int secondCount;
private int secondPending;
private async void buttonRound1_Click(object sender, EventArgs e)
{
await LongTermOpAsync();
}
private async void buttonRound2_Click(object sender, EventArgs e)
{
await Task.Run(async() =>
{
int delay;
int thisId;
lock (mtx2)
{
delay = rnd.Next(2000, 10000);
thisId = secondCount++;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label2Gen.Text = $"Generating second run delay #{thisId} of {delay} ms"
#if OLDSCHOOL_INVOKE
))
#endif
;
++secondPending;
}
await Task.Delay(delay);
lock (mtx2)
{
--secondPending;
#if OLDSCHOOL_INVOKE
Invoke(new Action(() =>
#endif
label2Gen.Text = $"Second run #{thisId} completed, {secondPending} pending..."
#if OLDSCHOOL_INVOKE
))
#endif
;
}
});
}
private void buttonRound12_Click(object sender, EventArgs e)
{
buttonRound1_Click(sender, e);
buttonRound2_Click(sender, e);
}
private bool isRunning = false;
private async void buttonCycle_Click(object sender, EventArgs e)
{
isRunning = !isRunning;
await Task.Run(() =>
{
while (isRunning)
{
buttonRound12_Click(sender, e);
Application.DoEvents();
}
});
}
}
}
Вы можете обновлять пользовательский интерфейс только из потока, создавшего пользовательский интерфейс. –
Будет сложно получить точный ответ на вашу ситуацию, не публикуя код нарушения. –
Было бы полезно, если бы вы опубликовали фактический код, создающий неожиданное поведение, а также полную информацию об ошибках. В противном случае вы получите очень общие ответы/предложения/рекомендации, которые могут не распространяться на ваш конкретный случай. – sstan