Почему не появляется "System.InvalidOperationException"?

Недавно начал изучать Thread и Invoke и столкнулся с проблемой. Почему следующий код выполняется без Exception:

private void button1_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(DoStuff1);
    thread.Name = "new thread";
    thread.Start();
}

private void DoStuff1()
{
    this.label1.Invoke((MethodInvoker)(() => label1.Text = "info from new thread"));
    button1.BackColor = Color.Red;
    DoStuff2();
}

private void DoStuff2()
{
    label1.Text = "info from new thread";
}

Интересует почему тело метода DoStuff2 выполняется, ведь мы все же обращаемся к контролу который был создан в другом потоке. И, кстати, если в DoStuff2 сам текст немного изменить, например на info from new thread2, то уже будет Exception. Это какая-то особенность языка, он проверяет что сам текст не меняется и не вызывает присваивание текста? И почему также выполняется без Exception button1.BackColor = Color.Red, здесь мы ведь тоже обращаемся к контролу?


Ответы (1 шт):

Автор решения: aepot

Свойства некоторых контролов не защищены от изменений из потоков, только и всего. Возможно это сделано в целях оптимизации, потому что на проверку контекста синхронизации все равно уходило бы какое-то время, возможно по каким-то другим причинам.

То есть обращение таким образом к Label допустимо, но целостность данных в свойстве Text - ваша ответственность, так как вероятные проблемы при конкурентном редактировании свойства этого контрола никуда не делись. .NET вам просто разрешает это сделать.

По сути исключения при обращении к контролам из стороннего потока берутся не из ниоткуда, а только там, где разработчики .NET эту проверку встроили, чтобы обезопасить нас от самых популярных ошибок при работе в условиях многопоточности.

В WPF есть пример, где подобное исключение возникает при использовании обычной коллекции - ObservableCollection, при использовании ее в контексте синхронизации UI. При попытке модифицировать коллекцию из стороннего потока выскочит NotSupportedException, причина - ровно та же, что и у вас в вопросе. А это ведь вообще не контрол.

Так что избавляйтесь от шаблона, что это некая особенность контролов - не давать себя менять из потоков, это просто такое решение разработчиков .NET, где добавить проверку, а где - нет.

→ Ссылка