Обновление с большой задержкой
Я принимаю ответ MUG4N на этот вопрос и хочу ответить на некоторые критические замечания, высказанные против него.
ChrisF сказал:
... вы не можете делать вызовы пользовательского интерфейса напрямую из фоновых потоков.
Это общий оператор, и он не на 100% верен.Позвольте мне лишь указать на несколько фактов:
На самом деле вы можете делать вызовы пользовательского интерфейса все, что вы хотите , если вы установите Control.CheckForIllegalCrossThreadCalls = false
. «Ack!» Я слышу, как ты говоришь. «Не никогда не делайте этого!» Да, да - но почему ?Ответ: потому что иногда это приведет к повреждению памяти.
Управляющие классы в System.Windows.Forms
не записаны как поточно-ориентированные, поэтому иногда их обновление из фоновых потоков может повредить память.Но если это только иногда происходит, а не всегда , то это говорит мне о том, что это не вызов кода UI per se , а скореепотенциально небезопасная коллизия кода пользовательского интерфейса, которая может вызвать исключения .
Чтобы укрепить пункт 1, рассмотрим это: «безопасный» способ вызова кода пользовательского интерфейсаиз фонового потока это сделать, используя Control.Invoke
или Control.BeginInvoke
, верно? Но это является вызовом пользовательского интерфейса ;это просто пользовательский интерфейс, который мы должны сделать, если мы обновляем GUI из потока, не являющегося GUI.Я имею в виду, очевидно, что это не просто вызов «любого» метода для объекта Control
из внешнего потока, который вызовет хаос (если бы это было так, то мы не могли бы даже вызвать Invoke
, и мызастрял бы полностью).Опять же, это потенциальная коллизия отдельных вызовов пользовательского интерфейса, которые не могут безопасно происходить одновременно, что приведет к разрушительным последствиям.
Имея в виду две вышеупомянутые точки, спросите себя: почему было бы небезопасно звонитьMessageBox.Show
из потока без графического интерфейса?Полностью отдельный Form
создается и отображается;его свойства никоим образом не взаимодействуют с любым другим существующим объектом GUI;на самом деле, он не может быть доступен где-нибудь в любым способом , кроме одного: из вызывающего потока, который получает доступ к свойству DialogResult
(и только через метод Show
).возвращаемое значение).
Перемещение.Конрад Альбрехт сказал:
... учитывая утверждение, что Show () настраивает свою собственную рассылку сообщений в теме рефана Дэна (которая не была подтверждена, но которую я не могу опровергнуть)...
Это совершенно справедливо (хотя я лично высоко ценю Джареда Пара, что я не склонен к сомнению в его словах).В любом случае, взгляд на метод MessageBox.Show
через Reflector показывает этот фрагмент:
Application.BeginModalMessageLoop();
try
{
result = Win32ToDialogResult(SafeNativeMethods.MessageBox(new HandleRef(owner, zero), text, caption, type));
}
finally
{
Application.EndModalMessageLoop();
UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);
}
Дальнейший взгляд на метод Application.BeginModalMessageLoop
показывает это:
ThreadContext.FromCurrent().BeginModalMessageLoop(null);
И это ThreadContext.FromCurrent
, в свою очередь:
// [Reflector shows that currentThreadContext is a ThreadStatic member. -Dan]
if (currentThreadContext == null)
{
currentThreadContext = new Application.ThreadContext();
}
return currentThreadContext;
Я недостаточно знаю об этих низкоуровневых конструкциях Windows, чтобы полностью понять этот код, но мне кажется, что это свидетельствует оименно то, что говорил Джаред в ответе, на который я ссылался в своем старом комментарии (для любопытных читателей: Является ли MessageBox.Show () автоматически маршалировать в поток пользовательского интерфейса? ).
Итак, да.Я полностью согласен с MUG4N по этому вопросу.
(Если кто-то может убедительно доказать, что я все еще здесь ошибаюсь, пожалуйста, говорите. Хотя я чувствую, что сделал довольно хорошее объяснение, почему я верю в MUG4NПравильно, я, очевидно, не уверен на 100%.)
Оригинальный вопрос
Часто вы просто хотите уведомить пользователя о том, что что-то произошло, но на самом деле в этом нет необходимости.вход от них.В этом распространенном сценарии я иногда вижу такой код:
MessageBox.Show("Something has occurred", "Something", MessageBoxButtons.OK);
Этот код, как мы все знаем, вызывает появление небольшого всплывающего окна только с кнопкой OK . Теперь вот что: этот код блокирует (поток пользовательского интерфейса). Но в подавляющем большинстве случаев, мне кажется, что если у вас только есть кнопка OK , блокировка не требуется. (Разве цель блокирования обычно не заключается в получении какого-либо ввода от пользователя? И если выбор пользователя only"OK", то в этом типичном случае блокирование не является довольно бессмысленным?)
Очевидно, я мог бы написать свою собственную маленькую форму, которая в основном делает то же, что делает MessageBox.Show
, за исключением того, что она ничего не возвращает (нет DialogResult
) и не блокирует. Но мне было просто интересно, существует ли что-то подобное, о чем я не знал.