Долгосрочное обновление
Я принимаю MUG4N ответ на этот вопрос, и я также хочу ответить на некоторые из критических замечаний, которые были высказаны против него.
ChrisF сказал:
... вы не можете делать UI-вызовы непосредственно из фоновых потоков.
Это пустой оператор и не является 100% истинным. Позвольте мне указать на несколько фактов:
-
Фактически вы можете сделать UI-вызовы, если вы установите
Control.CheckForIllegalCrossThreadCalls = false
. "Ack!" Я слышу, как ты говоришь. "Не никогда делать это!" Да, да - но почему? Ответ: поскольку иногда это приведет к повреждению памяти.Контрольные классы в
System.Windows.Forms
не записываются в потокобезопасные, поэтому иногда их обновление из фоновых потоков может привести к повреждению памяти. Но если это случается иногда и не всегда, то это говорит о том, что это не вызов кода пользовательского интерфейса как такового, а скорее потенциально опасное столкновение кода пользовательского интерфейса, которое может вызывать исключения. -
Чтобы укрепить точку 1, рассмотрите это: "безопасный" способ вызвать код пользовательского интерфейса из фонового потока - это сделать с помощью
Control.Invoke
илиControl.BeginInvoke
, правильно? Но это вызов пользовательского интерфейса; это просто вызов пользовательского интерфейса, который мы должны сделать, если мы обновляем графический интерфейс из потока, отличного от GUI. Я имею в виду, что это не просто вызов "любого" метода для объектаControl
из внешнего потока, который может вызвать хаос (если это так, то мы не могли даже позвонитьInvoke
, и мы полностью застрял). Опять же, это потенциальное столкновение отдельных вызовов пользовательского интерфейса, которые не могут безопасно происходить одновременно, что окажется деструктивным. -
Удерживая вышеуказанные две точки, спросите себя: почему было бы небезопасно вызывать
MessageBox.Show
из потока без GUI? Создается и отображается полностью отдельныйForm
; его свойства никоим образом не взаимодействуют с каким-либо другим существующим объектом GUI; на самом деле он не может быть доступен нигде каким-либо образом, кроме одного: из вызывающего потока, который обращается к свойствуDialogResult
(и только это через возвращаемое значение методаShow
).
Перемещение. Конрад Альбрехт сказал:
... учитывая утверждение, что Show() устанавливает свой собственный насос сообщений в теме Dan ref'd (что не было обосновано, но которое я не могу опровергнуть)...
Это совершенно справедливый вопрос (хотя я лично лично занимаю Джареда Пар с достаточно высоким уважением, что я, как правило, не склонен сомневаться в том, что он говорит). В любом случае, просмотр в 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);
Этот код, как мы все знаем, вызывает появление небольшого всплывающего окна только с кнопкой ОК. Теперь вот что: этот код блокирует (поток пользовательского интерфейса). Но в подавляющем большинстве случаев мне кажется, что если у вас есть кнопка ОК, очень мало нужно блокировать. (Не является ли целью блокировки обычно получать некоторый ввод от пользователя? И если выбор пользователя только "ОК" в этом типичном случае не блокирует довольно бессмысленно?)
Очевидно, я мог бы написать свою собственную небольшую форму, которая в основном выполняет именно то, что делает MessageBox.Show
, за исключением того, что он ничего не возвращает (no DialogResult
) и не блокирует. Но мне было просто интересно, существует ли что-то подобное, о котором я не знал.