Label.Text = Struct.Value (Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException)
У меня есть приложение, которое я работаю над использованием опросов от интернет-провайдера (квота на скачивание). Я пытался проделать это через 'new Thread(ThreaProc)', но это не сработало, сейчас пытаюсь использовать подход на основе IAsyncResult, который делает то же самое... Я понятия не имею, как исправить, пожалуйста, помогите?
Нужно знать:
// Global
public delegate void AsyncPollData(ref POLLDATA pData);
// Class scope:
private POLLDATA pData;
private void UpdateUsage()
{
AsyncPollData PollDataProc = new AsyncPollData(frmMain.PollUsage);
IAsyncResult result = PollDataProc.BeginInvoke(ref pData,
new AsyncCallback(UpdateDone), PollDataProc);
}
public void UpdateDone(IAsyncResult ar)
{
AsyncPollData PollDataProc = (AsyncPollData)ar.AsyncState;
PollDataProc.EndInvoke(ref pData, ar);
// The Exception occurs here:
lblStatus.Text = pData.LastError;
}
public static void PollUsage(ref POLLDATA PData)
{
PData.LastError = "Some string";
return;
}
5 ответов
Вы можете создать себе новый класс и создать такие расширения:
public static class ThreadSafeHelpers {
public static void SetText(this Label varLabel, string newText) {
if (varLabel.InvokeRequired) {
varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
} else {
varLabel.Text = newText;
}
}
}
И затем вы используете это в любом месте вашего кода, как это:
lblStatus.SetText(pData.LastError);
Вы можете создать несколько похожих расширений для других вещей, таких как CheckBox
, RadioButtons
в том же классе. Таким образом, вы можете легко запомнить и использовать методы расширения.
Конечно, вы также можете создать нормальный метод, как этот (обратите внимание на отсутствие this
рядом с ярлыком):
public static class ThreadSafeHelpers {
public static void SetText(Label varLabel, string newText) {
if (varLabel.InvokeRequired) {
varLabel.BeginInvoke(new MethodInvoker(() => SetText(varLabel, newText)));
} else {
varLabel.Text = newText;
}
}
}
и используйте такой код:
ThreadSafeHelpers.SetText(varLabel, newText);
lblStatus.Invoke(delegate() { lblStatus.Text = pData.LastError; });
Обновление значений между потоками небезопасно, поэтому компилятор предупреждает вас. Используя Invoke()
переданный код будет вызван в потоке GUI, поэтому вы обновляете значение GUI в потоке GUI, что безопасно.
Ваш элемент управления был создан в потоке A [Тот, который рисует и обрабатывает сообщения Windows], поэтому поток B [Монитор] не может получить к нему доступ [Ура для условий гонки], взгляните на это:
Как обновить графический интерфейс из другого потока в C#?
ура
Вам может подойти метод Thread Sleep
Thread.Sleep(100);
this.Invoke((MethodInvoker)delegate
{
txtChatBox.Text += msgReceived.strMessage + "\r\n";
});
[Больше примечаний: я использую.NET 3.0]
Даже когда я использую метод, описанный в "Как обновить графический интерфейс из другого потока в C#?" другие части этой функции (UpdateDone) не работают (даже части, которые не имеют ничего общего с многопоточностью, не работают, потому что некоторые части класса были доступны вне текущего потока).
// This now works
DelegationClass.SetPropertyThreadSafe(lblStatus, () => lblStatus.Text, pData.LastError);
// Later on down the function, both objects are a member of the same class, the variable icon was never touched by another thread.
this.Icon = icon;
// An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
// Additional information: Cross-thread operation not valid: Control 'frmMain' accessed from a thread other than the thread it was created on.
Поправьте меня, если я ошибаюсь, но поскольку вызывается EndInvoke, поток должен был прерваться. Поэтому не должно быть никаких "условий гонки"; все данные снова принадлежат первичному потоку, и я могу свободно делать то, что я хочу с данными...?
В этой структуре есть несколько элементов, которые я использую в классе; Есть ли лучший способ сделать это? Как бы вы это сделали?
Основные ограничения:
* Отдельная функция выполняет опрос данных, идея не в том, чтобы блокировать взаимодействие с пользовательским интерфейсом.
* Поэтому эта функция должна быть асинхронной
* Основной класс Form (frmMain) должен быть уведомлен (Async) о завершении функции, о которой идет речь, и при необходимости обновить ее графический интерфейс.
* И, конечно же, прежде всего, данные, полученные с помощью функции, должны быть легко доступны членам frmMain.
(Боже, многопоточность была намного проще с C++)