Показване на начален екран, докато се изпълнява връзка с база данни (която може да отнеме много време).

Опитвам се да покажа начален екран и да не блокирам приложението, докато се свързва с база данни. Нормалните връзки (към MSSQL чрез ADO) отнемат около 300 msec и това не кара основната нишка да показва „не отговаря“ на Windows.

Въпреки това, в случай на (а) мрежова грешка или (б) грешка в конфигурацията (невалидно име на хост/екземпляр на 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, което считам за екстремна миризма на код. (Това може да е допустимо изключение от това правило)

B. Въвеждам нишки при стартиране на приложение и се притеснявам от въвеждане на грешки.

Ако някой има референтна реализация, за която е известно, че е свободна от състезателни условия, която може да направи фонова връзка с ADO и е известно, че е безопасен подход, това би било наистина полезно. В противен случай общите съвети или частичните примери са добри.


person Warren P    schedule 21.05.2012    source източник
comment
Като се има предвид, че нормалната връзка отнема само 300 ms, защо не промените времето за изчакване (напр. 1000 ms)?   -  person awmross    schedule 21.05.2012
comment
Как да покажа начален екран от фонова нишка: stackoverflow.com/questions/388506/   -  person Harriv    schedule 21.05.2012
comment
Този проблем е един от основните случаи на употреба за показване на потребителски интерфейс от нишка, различна от основната GUI нишка   -  person David Heffernan    schedule 21.05.2012
comment
@David, или ако не използвате компоненти за данни, преместете и стартирайте неща от базата данни в работни нишки. Това е препоръчително, когато изпълнявате отнемащи време заявки и искате да работите с потребителския интерфейс на формуляра по някаква причина (не само да блокирате потребител с някаква чакаща анимация). Този модел успешно използвах за инструмент, който периодично проверяваше базата данни, докато потребителят можеше да работи с останалата част от приложението. Но трябва да кажа, че тук би била подходяща формата на работна нишка.   -  person TLama    schedule 21.05.2012
comment
За да създадете формуляр без зависимост от VCL и подходящ за нишки, вижте Създаване на формуляри без използване на VCL или Началният екран с резба и модалните диалогови прозорци на Peter Below.   -  person LU RD    schedule 21.05.2012
comment
Вашият цикъл while ще работи на 100% CPU. Можете да го накарате да не работи с MsgWaitForMultipleObjects според моя отговор тук: stackoverflow.com/questions/10334553/   -  person David Heffernan    schedule 21.05.2012
comment
Цикълът while не работи на 100% CPU, защото 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

Има няколко метода за комуникация между нишки. Обикновено използвам съобщенията на Window. Първо дефинирам потребителско съобщение и манипулатор на съобщения във формуляр. След това, когато трябва да информирам основната нишка от персонализирана нишка, използвам функцията PostMessage, за да изпратя известие.

Малък урок тук.

Можете също да използвате библиотека за нишки, напр. OmniThreadLibrary.

person Harriv    schedule 21.05.2012
comment
Знам. НО се чудя конкретно за блокиране на стартирането на приложението ми -- как да го направя - собствен цикъл на изпомпване на съобщения (извикване на съобщения за application.process) или нещо друго? - person Warren P; 22.05.2012
comment
Накрая показах начален екран в нишката. Вижте връзката в коментара ми към въпроса. - person Harriv; 25.05.2012