В блогах есть сотни примеров и т.д. о том, как реализовать фоновый рабочий, который регистрирует или передает статус элементу интерфейса переднего плана. Большинство из них включают подход к обработке состояния гонки, который существует между нерестом рабочего потока, и создание диалога переднего плана с помощью ShowDialog(). Тем не менее, мне показалось, что простой подход заключается в том, чтобы заставить создание дескриптора в конструкторе формы, чтобы поток не смог вызвать вызов Invoke/BeginInvoke в форме до создания его дескриптора.
Рассмотрим простой пример класса Logger, который использует фоновый рабочий поток для входа на передний план.
Предположим также, что мы не хотим, чтобы NLog или какая-либо другая тяжелая структура делала что-то настолько простое и легкое.
Окно моего регистратора открывается с помощью ShowDialog() потоком переднего плана, но только после того, как начат поток "рабочий" фона. Рабочий поток вызывает logger.Log(), который сам использует logForm.BeginInvoke(), чтобы правильно обновить управление журналом в потоке переднего плана.
public override void Log(string s)
{
form.BeginInvoke(logDelegate, s);
}
Где logDelegate - просто простая оболочка вокруг "form.Log()" или какой-либо другой код, который может обновить индикатор выполнения.
Проблема заключается в существовании условия расы; когда поток рабочего фона начинает регистрироваться перед передним планом ShowDialog() называется формой Handle еще не создана, поэтому вызов BeginInvoke() завершается с ошибкой.
Я знаком с различными подходами, в том числе с использованием события Form OnLoad и таймера для создания рабочей задачи, приостановленной до тех пор, пока событие OnLoad не генерирует сообщение таймера, которое запускает задачу после отображения формы или, как уже упоминалось, используя очередь для сообщений. Тем не менее, я думаю, что просто принудительное создание дескриптора диалога для создания раннего (в конструкторе) гарантирует отсутствие условия гонки, предполагая, что поток генерируется тем же потоком, который создает диалог.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.handle(v=vs.71).aspx
MSDN говорит: "Если дескриптор еще не создан, ссылка на это свойство заставит дескриптор быть создан".
Итак, мой журнал закрывает форму, и ее конструктор делает:
public SimpleProgressDialog() {
var h = form.Handle; // dereference the handle
}
Решение кажется слишком простым, чтобы быть правильным. Меня особенно интересует, почему, казалось бы, слишком простое решение или небезопасно использовать.
Любые комментарии? Я что-то пропустил?
EDIT: Я не прошу альтернатив. Не спрашивая, как использовать NLog или Log4net и т.д., Если бы я был, я бы написал страницу обо всех ограничениях для клиента в этом приложении и т.д.
По количеству upvotes, есть много других людей, которые хотели бы знать ответ тоже.