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++)

Другие вопросы по тегам