Показывать заставку во время подключения к базе данных (которое может занять много времени)

Я пытаюсь показать заставку, а не замораживать приложение, пока оно подключается к базе данных. Обычные подключения (к MSSQL через ADO) занимают около 300 мс, и это не приводит к тому, что основной поток показывает «не отвечает» в окнах.

Однако в случае (а) сетевой ошибки или (б) ошибки конфигурации (недопустимое имя хоста / экземпляра SQL-сервера) время ожидания истекает через 60 секунд. Это не только делает приложение невосприимчивым, но и практически невозможно показать какую-либо ошибку или сообщение, когда оно собирается зависнуть. Я мог бы вывести сообщение перед установкой соединения, но на самом деле нет решения, когда основной поток блокируется на 60 секунд.

Похоже, решение состоит в том, чтобы переместить соединение в фоновый поток. Это привело к следующему коду:

  1. класс TThread, который устанавливает фоновое соединение, и некоторый SyncObj, например TEvent, используемый для отправки сигнала обратно в основной поток.

  2. Цикл в основном потоке с этим кодом:

    BackgroundThread.StartConnecting;
    while not BackgroundThread.IsEventSignalled do begin
       Application.ProcessMessages; // keep message pump alive.
    end;
    // continue startup (reports error if db connection failed)
    

Это правильный путь? Мои сомнения связаны со следующими элементами вышеуказанного решения:

А. Я бы вызвал Application.ProcessMessages, что считаю сильным запахом кода (это может быть допустимым исключением из этого правила).

Б. Я добавляю потоки при запуске приложения и беспокоюсь о том, чтобы внести ошибки.

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


person Warren P    schedule 21.05.2012    source источник
comment
Учитывая, что обычное соединение занимает всего 300 мс, почему бы не изменить тайм-аут (например, 1000 мс)?   -  person awmross    schedule 21.05.2012
comment
Как отобразить заставку из фонового потока: stackoverflow.com/questions/388506/   -  person Harriv    schedule 21.05.2012
comment
Эта проблема является одним из основных вариантов использования для отображения пользовательского интерфейса из потока, отличного от основного потока графического интерфейса.   -  person David Heffernan    schedule 21.05.2012
comment
@David, или, если вы не используете компоненты, поддерживающие данные, перемещайте и запускайте файлы базы данных в рабочих потоках. Это рекомендуется, когда вы выполняете трудоемкие запросы и по какой-то причине хотите работать с пользовательским интерфейсом формы (а не только блокировать пользователя с помощью некоторой ожидающей анимации). Эту модель я успешно использовал для инструмента, который периодически опрашивал базу данных, пока пользователь мог работать с остальной частью приложения. Но должен сказать, что здесь будет подходящая форма рабочего потока.   -  person TLama    schedule 21.05.2012
comment
Чтобы создать форму, не зависящую от VCL и подходящую для потоков, см. Создание форм без использования VCL или Резьбовой экран-заставка и модальные диалоги Питера Ниже.   -  person LU RD    schedule 21.05.2012
comment
Ваш цикл while будет работать на 100% ЦП. Вы можете заставить его бездействовать с помощью MsgWaitForMultipleObjects в соответствии с моим ответом здесь: stackoverflow.com/questions/10334553/   -  person David Heffernan    schedule 21.05.2012
comment
Цикл while не выполняется на 100% ЦП, потому что IsEventSignalled выполняет TEvent.WaitEvent (50), что означает, что он возвращает не более 20 раз в секунду. Этого, в свою очередь, достаточно, чтобы цикл сообщений продолжал работать. (TEvent.WaitForEvent == ›Win32 WaitForSingleObject)   -  person Warren P    schedule 22.05.2012
comment
@awmross; Я сделал это, и это действительно помогает. Единственный раз, когда это было бы плохо, - это если соединение, которое должно было быть успешным, заняло 6 секунд. Но у меня не может быть всего, не так ли? :-)   -  person Warren P    schedule 22.05.2012


Ответы (2)


Из-за известного ограничения, заключающегося в том, что каждый поток должен использовать собственное соединение ADO (т. Е. Вы не можете использовать объект соединения, созданный в другом потоке), единственный вариант, о котором я могу думать, - это создать поток, который будет подключаться к базе данных, а затем соединение установлено или истекло время ожидания, сообщить основному потоку о событии и закрыть / уничтожить соединение. Тем временем основной поток может отображать заставку или прогресс, ожидая сообщения от этого потока. Итак, вы исключили случай с неправильным паролем или недоступным хостом. Есть разумное предположение, что, если второй поток сможет подключиться, основной поток сможет подключиться сразу после этого.

person Silver    schedule 22.05.2012
comment
+1, согласен, но вам нужно будет переместить все действия с базой данных в этот поток, потому что вы не можете передать объект подключения в основной поток из вашего рабочего. И необходимо, чтобы они были в основном потоке, например. в случае, если вы используете компоненты, поддерживающие данные. - person TLama; 22.05.2012
comment
Он говорит, что вы только открываете, а потом просто закрываете это соединение. Таким образом, вы обрабатываете только начальное соединение как функцию проверки ошибок вашего приложения. Вам все равно придется переделывать логику подключения в основном потоке. - person Warren P; 23.05.2012
comment
@Warren, если вы собираетесь только проверить, возможно ли вообще подключиться, а затем повторно подключиться с теми же настройками подключения в основном потоке, вы можете использовать код из моего удаленного (теперь действительно удаленного) ответа. - person TLama; 24.05.2012

Есть несколько методов межпоточного взаимодействия. Обычно я использую сообщения Windows. Сначала я определяю индивидуальное сообщение и обработчик сообщений в форме. Затем, когда мне нужно сообщить основному потоку из настраиваемого потока, я использую функцию PostMessage для отправки уведомления.

Небольшой учебник здесь.

Вы также можете использовать библиотеку для потоковой передачи, например OmniThreadLibrary.

person Harriv    schedule 21.05.2012
comment
Я знаю. Но меня конкретно интересует блокировка запуска моего приложения - как мне это сделать - мой собственный цикл перекачки сообщений (вызов сообщений application.process) или что-то еще? - person Warren P; 22.05.2012
comment
Я закончил тем, что показывал заставку в ветке .. См. Ссылку в моем комментарии к вопросу. - person Harriv; 25.05.2012